diff --git a/.github/workflows/update_pipeline_diagrams.yaml b/.github/workflows/update_pipeline_diagrams.yaml new file mode 100644 index 000000000..ebe090790 --- /dev/null +++ b/.github/workflows/update_pipeline_diagrams.yaml @@ -0,0 +1,49 @@ +name: Update Pipeline Diagrams + +on: + push: + branches: [main] + +permissions: + contents: write + +jobs: + update-diagrams: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install Claude Code CLI + run: npm install -g @anthropic-ai/claude-code + + - name: Install diagram dependencies + working-directory: docs/pipeline-diagrams + run: npm ci + + - name: Run diagram update pipeline + working-directory: docs/pipeline-diagrams + run: node scripts/update-diagrams.mjs + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + DIFF_BASE: ${{ github.event.before }} + DIFF_HEAD: ${{ github.event.after }} + + - name: Commit if changed + run: | + if git diff --quiet docs/pipeline-diagrams/; then + echo "No diagram changes detected." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add docs/pipeline-diagrams/public/dots/ docs/pipeline-diagrams/manifest.json + git commit -m "Auto-update pipeline diagrams" + git push diff --git a/Makefile b/Makefile index 606a9ad9c..0e0c18a44 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all format test install download upload docker documentation data validate-data calibrate calibrate-build publish-local-area upload-calibration upload-dataset upload-database push-to-modal build-data-modal build-matrices calibrate-modal calibrate-modal-national calibrate-both stage-h5s stage-national-h5 stage-all-h5s pipeline validate-staging validate-staging-full upload-validation check-staging check-sanity clean build paper clean-paper presentations database database-refresh promote-database promote-dataset promote build-h5s validate-local +.PHONY: all format test install download upload docker documentation data validate-data calibrate calibrate-build publish-local-area upload-calibration upload-dataset upload-database push-to-modal build-data-modal build-matrices calibrate-modal calibrate-modal-national calibrate-both stage-h5s stage-national-h5 stage-all-h5s pipeline validate-staging validate-staging-full upload-validation check-staging check-sanity clean build paper clean-paper presentations database database-refresh promote-database promote-dataset promote build-h5s validate-local diagrams GPU ?= T4 EPOCHS ?= 1000 @@ -288,3 +288,6 @@ presentations/nta_2024_11/nta_2024_slides.pdf: presentations/nta_2024_11/main.te cd presentations/nta_2024_11 && \ pdflatex -jobname=nta_2024_slides main && \ pdflatex -jobname=nta_2024_slides main + +diagrams: + cd docs/pipeline-diagrams && npm run dev diff --git a/docs/pipeline-diagrams/.gitignore b/docs/pipeline-diagrams/.gitignore new file mode 100644 index 000000000..64a8cfe19 --- /dev/null +++ b/docs/pipeline-diagrams/.gitignore @@ -0,0 +1,15 @@ +# Dependencies +node_modules + +# Next.js +.next +out + +# Logs +*.log + +# Editor +.DS_Store +.idea +.vscode +*.sw? diff --git a/docs/pipeline-diagrams/CLAUDE.md b/docs/pipeline-diagrams/CLAUDE.md new file mode 100644 index 000000000..970160eec --- /dev/null +++ b/docs/pipeline-diagrams/CLAUDE.md @@ -0,0 +1,71 @@ +# Pipeline Diagram DOT Style Guide + +When editing or creating Graphviz DOT files in `public/dots/`, follow these conventions exactly. + +## Graph Defaults + +```dot +digraph "Title" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] +} +``` + +## Node Types + +Each node type has a specific color and style. Always use `style="filled,rounded"` unless noted. + +| Type | Fill | Border | Style | Use for | +|------|------|--------|-------|---------| +| Input | `fillcolor="#dbeafe" color="#3b82f6"` | blue | filled,rounded | Data files consumed by this stage | +| Output | `fillcolor="#dcfce7" color="#22c55e"` | green | filled,rounded | Data files produced by this stage | +| Process | `fillcolor="#ffedd5" color="#f97316"` | orange | filled,rounded | Computation/transformation steps | +| Utility | `fillcolor="#f3e8ff" color="#a855f7"` | purple | filled,rounded | Shared helper functions/modules | +| External | `fillcolor="#fef9c3" color="#eab308"` | yellow | filled,rounded | External services (HuggingFace, Modal, APIs) | +| US-Specific | `fillcolor="#fce7f3" color="#ec4899"` | pink | filled,rounded | Steps unique to the US pipeline | +| UK-Specific | `fillcolor="#ccfbf1" color="#14b8a6"` | teal | filled,rounded | Steps unique to the UK pipeline | +| Missing | `fillcolor="#fee2e2" color="#ef4444"` | red | filled,rounded,dashed | Planned but not yet implemented | +| Absent | `fillcolor="#f3f4f6" color="#d1d5db"` | gray | filled,rounded,dashed | Exists in the other country's pipeline but not this one | + +## Edge Types + +| Type | Attributes | Use for | +|------|-----------|---------| +| Data flow | `color="#334155" penwidth=2 style="solid"` | Primary data movement between steps | +| Produces artifact | `color="#16a34a" penwidth=2 style="solid"` | Step producing an output file | +| Uses utility | `color="#7c3aed" penwidth=1.5 style="dashed"` | Step calling a shared utility | +| External source | `color="#b45309" penwidth=1.5 style="dotted"` | Data coming from external service | +| Runs on infra | `color="#dc2626" penwidth=1.5 style="dashed"` | Infrastructure dependency (Modal, GPU) | +| Informational | `color="#9ca3af" penwidth=1 style="dotted"` | Contextual/optional relationship | + +Edge labels use `fontcolor` matching the edge `color`: +```dot + a -> b [color="#334155" penwidth=2 style="solid" label="description" fontcolor="#334155"] +``` + +## Node Label Format + +Use HTML-like labels with a `` structure: + +```dot + node_id [label=<
+ + + +
Node Title
Subtitle or function name
Detail line 1
Detail line 2
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] +``` + +- **First row**: Bold title — the node's name +- **Second row**: Italic subtitle — function name, file path, or brief description (point size 9, color #666666) +- **Third row** (optional): Details — implementation notes, data formats (point size 8, color #555555). Use `
` for line breaks. + +## Node ID Conventions + +- Use snake_case: `in_cps`, `qrf_pass1`, `out_ext` +- Prefix inputs with `in_`, outputs with `out_`, utilities with `util_`, missing items with `miss_`, absent items with `absent_` +- Use descriptive but concise IDs + +## Manifest + +The file `manifest.json` in this directory is the source of truth for which stages exist, their titles, and which Python source files map to each stage. The Sidebar and page components derive their data from this manifest. diff --git a/docs/pipeline-diagrams/README.md b/docs/pipeline-diagrams/README.md new file mode 100644 index 000000000..a36934d87 --- /dev/null +++ b/docs/pipeline-diagrams/README.md @@ -0,0 +1,16 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/docs/pipeline-diagrams/app/[country]/page.jsx b/docs/pipeline-diagrams/app/[country]/page.jsx new file mode 100644 index 000000000..0c262ec42 --- /dev/null +++ b/docs/pipeline-diagrams/app/[country]/page.jsx @@ -0,0 +1,22 @@ +"use client"; + +import { use } from "react"; +import Diagram from "../../components/Diagram"; +import manifest from "../../manifest.json"; + +const overviewTitles = Object.fromEntries( + Object.entries(manifest.countries).map(([country, data]) => [ + country, + data.overviewTitle, + ]) +); + +export default function CountryOverview({ params }) { + const { country } = use(params); + return ( + + ); +} diff --git a/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.jsx b/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.jsx new file mode 100644 index 000000000..b66a95c16 --- /dev/null +++ b/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.jsx @@ -0,0 +1,24 @@ +"use client"; + +import { use } from "react"; +import Diagram from "../../../../components/Diagram"; +import manifest from "../../../../manifest.json"; + +const titlesByCountry = Object.fromEntries( + Object.entries(manifest.countries).map(([country, data]) => [ + country, + Object.fromEntries(data.stages.map((s) => [s.id, s.title])), + ]) +); + +export default function StagePage({ params }) { + const { country, stageId } = use(params); + const titles = titlesByCountry[country] || {}; + const title = titles[stageId] || `Stage ${stageId}`; + return ( + + ); +} diff --git a/docs/pipeline-diagrams/app/globals.css b/docs/pipeline-diagrams/app/globals.css new file mode 100644 index 000000000..f1d8c73cd --- /dev/null +++ b/docs/pipeline-diagrams/app/globals.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/docs/pipeline-diagrams/app/layout.jsx b/docs/pipeline-diagrams/app/layout.jsx new file mode 100644 index 000000000..30bd5c13f --- /dev/null +++ b/docs/pipeline-diagrams/app/layout.jsx @@ -0,0 +1,17 @@ +import "./globals.css"; +import Sidebar from "../components/Sidebar"; + +export const metadata = { + title: "PolicyEngine Data Pipelines", +}; + +export default function RootLayout({ children }) { + return ( + + + +
{children}
+ + + ); +} diff --git a/docs/pipeline-diagrams/app/page.jsx b/docs/pipeline-diagrams/app/page.jsx new file mode 100644 index 000000000..8da13a42f --- /dev/null +++ b/docs/pipeline-diagrams/app/page.jsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Home() { + redirect("/us"); +} diff --git a/docs/pipeline-diagrams/components/Diagram.jsx b/docs/pipeline-diagrams/components/Diagram.jsx new file mode 100644 index 000000000..04f5d784d --- /dev/null +++ b/docs/pipeline-diagrams/components/Diagram.jsx @@ -0,0 +1,192 @@ +"use client"; + +import { useEffect, useRef, useState, useCallback } from "react"; + +const MIN_SCALE = 0.1; +const MAX_SCALE = 5; +const ZOOM_STEP = 0.15; + +export default function Diagram({ dotPath, title }) { + const containerRef = useRef(null); + const viewportRef = useRef(null); + const [status, setStatus] = useState("loading"); + const [errorMsg, setErrorMsg] = useState(""); + const [scale, setScale] = useState(1); + const [pan, setPan] = useState({ x: 0, y: 0 }); + const dragRef = useRef({ dragging: false, startX: 0, startY: 0, startPanX: 0, startPanY: 0 }); + + // Fit the SVG so its full extent is visible in the viewport + const fitToView = useCallback(() => { + const svg = containerRef.current?.querySelector("svg"); + const viewport = viewportRef.current; + if (!svg || !viewport) return; + const vb = svg.viewBox.baseVal; + if (!vb || (!vb.width && !vb.height)) return; + const vw = viewport.clientWidth; + const vh = viewport.clientHeight; + const s = Math.min(vw / vb.width, vh / vb.height, 1); + setScale(s); + setPan({ + x: (vw - vb.width * s) / 2, + y: (vh - vb.height * s) / 2, + }); + }, []); + + useEffect(() => { + let cancelled = false; + + async function render() { + setStatus("loading"); + try { + const res = await fetch(dotPath); + if (!res.ok) throw new Error(`Failed to load ${dotPath}: ${res.status}`); + const dotString = await res.text(); + + const { Graphviz } = await import("@hpcc-js/wasm-graphviz"); + const gv = await Graphviz.load(); + + if (cancelled) return; + + const svg = gv.dot(dotString); + + if (cancelled) return; + if (containerRef.current) { + containerRef.current.innerHTML = svg; + const svgEl = containerRef.current.querySelector("svg"); + if (svgEl) { + // Preserve viewBox but let us control size via transform + const w = svgEl.getAttribute("width"); + const h = svgEl.getAttribute("height"); + if (!svgEl.viewBox.baseVal.width && w && h) { + svgEl.setAttribute("viewBox", `0 0 ${parseFloat(w)} ${parseFloat(h)}`); + } + svgEl.removeAttribute("width"); + svgEl.removeAttribute("height"); + svgEl.style.width = "100%"; + svgEl.style.height = "100%"; + svgEl.style.overflow = "visible"; + } + } + setStatus("ready"); + // Fit after a frame so the viewport has dimensions + requestAnimationFrame(() => { + if (!cancelled) fitToView(); + }); + } catch (e) { + if (!cancelled) { + console.error("Diagram render error:", e); + setErrorMsg(e.message); + setStatus("error"); + } + } + } + + render(); + return () => { + cancelled = true; + }; + }, [dotPath, fitToView]); + + // Wheel zoom — zoom toward cursor position + useEffect(() => { + const viewport = viewportRef.current; + if (!viewport) return; + + function onWheel(e) { + e.preventDefault(); + const rect = viewport.getBoundingClientRect(); + const cursorX = e.clientX - rect.left; + const cursorY = e.clientY - rect.top; + + setScale((prev) => { + const direction = e.deltaY < 0 ? 1 : -1; + const next = Math.min(MAX_SCALE, Math.max(MIN_SCALE, prev * (1 + direction * ZOOM_STEP))); + const ratio = next / prev; + setPan((p) => ({ + x: cursorX - ratio * (cursorX - p.x), + y: cursorY - ratio * (cursorY - p.y), + })); + return next; + }); + } + + viewport.addEventListener("wheel", onWheel, { passive: false }); + return () => viewport.removeEventListener("wheel", onWheel); + }, []); + + // Mouse drag to pan + const onMouseDown = useCallback((e) => { + if (e.button !== 0) return; + dragRef.current = { dragging: true, startX: e.clientX, startY: e.clientY, startPanX: pan.x, startPanY: pan.y }; + }, [pan]); + + useEffect(() => { + function onMouseMove(e) { + const d = dragRef.current; + if (!d.dragging) return; + setPan({ + x: d.startPanX + (e.clientX - d.startX), + y: d.startPanY + (e.clientY - d.startY), + }); + } + function onMouseUp() { + dragRef.current.dragging = false; + } + window.addEventListener("mousemove", onMouseMove); + window.addEventListener("mouseup", onMouseUp); + return () => { + window.removeEventListener("mousemove", onMouseMove); + window.removeEventListener("mouseup", onMouseUp); + }; + }, []); + + const zoomIn = () => { + setScale((s) => Math.min(MAX_SCALE, s * (1 + ZOOM_STEP))); + }; + const zoomOut = () => { + setScale((s) => Math.max(MIN_SCALE, s * (1 - ZOOM_STEP))); + }; + + const pct = Math.round(scale * 100); + + return ( +
+
+

{title}

+ {status === "ready" && ( +
+ + {pct}% + + +
+ )} +
+
+ {status === "loading" && ( +
+ Loading diagram... +
+ )} + {status === "error" && ( +
+ Error: {errorMsg} +
+ )} +
+
+
+ ); +} diff --git a/docs/pipeline-diagrams/components/Sidebar.jsx b/docs/pipeline-diagrams/components/Sidebar.jsx new file mode 100644 index 000000000..1863978ba --- /dev/null +++ b/docs/pipeline-diagrams/components/Sidebar.jsx @@ -0,0 +1,120 @@ +"use client"; + +import Link from "next/link"; +import { usePathname, useRouter } from "next/navigation"; +import { NODE_LEGEND, EDGE_LEGEND } from "./legends"; +import manifest from "../manifest.json"; + +function buildStageList(country) { + const c = manifest.countries[country]; + const overview = { path: `/${country}`, label: "Overview", desc: "Cross-stage data flow" }; + const stages = c.stages.map((s) => ({ + path: `/${country}/stage/${s.id}`, + label: s.label, + desc: s.desc, + absent: s.absent || false, + })); + return [overview, ...stages]; +} + +const usStages = buildStageList("us"); +const ukStages = buildStageList("uk"); + +export default function Sidebar() { + const router = useRouter(); + const pathname = usePathname(); + const isUK = pathname.startsWith("/uk"); + const country = isUK ? "uk" : "us"; + const stages = isUK ? ukStages : usStages; + + return ( + + ); +} diff --git a/docs/pipeline-diagrams/components/legends.js b/docs/pipeline-diagrams/components/legends.js new file mode 100644 index 000000000..916e1da24 --- /dev/null +++ b/docs/pipeline-diagrams/components/legends.js @@ -0,0 +1,25 @@ +export const NODE_LEGEND = [ + { color: "bg-blue-200 border-blue-400", label: "Input" }, + { color: "bg-green-200 border-green-500", label: "Output" }, + { color: "bg-orange-200 border-orange-400", label: "Process" }, + { color: "bg-purple-200 border-purple-400", label: "Utility" }, + { color: "bg-red-200 border-red-300 border-dashed", label: "Missing" }, + { color: "bg-yellow-200 border-yellow-400", label: "External" }, + { color: "bg-pink-200 border-pink-400", label: "US-Specific" }, + { color: "bg-teal-200 border-teal-400", label: "UK-Specific" }, + { color: "bg-gray-100 border-gray-300 border-dashed", label: "Absent" }, +]; + +export const EDGE_LEGEND = [ + { label: "Data flow", color: "#334155", dash: false, width: 2 }, + { + label: "Produces artifact", + color: "#16a34a", + dash: false, + width: 2, + }, + { label: "Uses utility", color: "#7c3aed", dash: true, width: 1.5 }, + { label: "External source", color: "#b45309", dash: true, width: 1.5 }, + { label: "Runs on infra", color: "#dc2626", dash: true, width: 1.5 }, + { label: "Informational", color: "#9ca3af", dash: true, width: 1 }, +]; diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/.gitignore b/docs/pipeline-diagrams/demos/pipeline-demos/.gitignore new file mode 100644 index 000000000..5ef6a5207 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/AGENTS.md b/docs/pipeline-diagrams/demos/pipeline-demos/AGENTS.md new file mode 100644 index 000000000..8bd0e3908 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/CLAUDE.md b/docs/pipeline-diagrams/demos/pipeline-demos/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/README.md b/docs/pipeline-diagrams/demos/pipeline-demos/README.md new file mode 100644 index 000000000..e215bc4cc --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/page.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/page.tsx new file mode 100644 index 000000000..14f5d7985 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/page.tsx @@ -0,0 +1,27 @@ +"use client"; + +import PipelineDiagram from "../components/PipelineDiagram"; +import pipelineData from "../pipeline.json"; + +// Overview: show all stages as a high-level flow +const overviewStage = { + id: -1, + label: "Overview", + title: "Pipeline Overview — Cross-Stage Data Flow", + description: "High-level view of all pipeline stages", + nodes: pipelineData.stages.map((s) => ({ + id: `stage_${s.id}`, + label: `${s.label}: ${s.description}`, + node_type: "process", + description: s.description, + })), + edges: pipelineData.stages.slice(0, -1).map((s, i) => ({ + source: `stage_${s.id}`, + target: `stage_${pipelineData.stages[i + 1].id}`, + edge_type: "data_flow", + })), +}; + +export default function CountryOverview() { + return ; +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/stage/[stageId]/page.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/stage/[stageId]/page.tsx new file mode 100644 index 000000000..23e5824e4 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/[country]/stage/[stageId]/page.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { use } from "react"; +import PipelineDiagram from "../../../components/PipelineDiagram"; +import pipelineData from "../../../pipeline.json"; + +export default function StagePage({ + params, +}: { + params: Promise<{ country: string; stageId: string }>; +}) { + const { stageId } = use(params); + const stageIdNum = parseInt(stageId, 10); + const stage = pipelineData.stages.find((s) => s.id === stageIdNum); + + if (!stage) { + return ( +
+ Stage {stageId} not found. +
+ ); + } + + return ; +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/colors.ts b/docs/pipeline-diagrams/demos/pipeline-demos/app/colors.ts new file mode 100644 index 000000000..abfdebaf3 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/colors.ts @@ -0,0 +1,25 @@ +// Node type → visual properties (matching pipeline_schema.py NODE_COLORS) +export const NODE_COLORS: Record = { + input: { fill: "#dbeafe", border: "#3b82f6" }, + output: { fill: "#dcfce7", border: "#22c55e" }, + process: { fill: "#ffedd5", border: "#f97316" }, + utility: { fill: "#f3e8ff", border: "#a855f7" }, + external: { fill: "#fef9c3", border: "#eab308" }, + us_specific: { fill: "#fce7f3", border: "#ec4899" }, + uk_specific: { fill: "#ccfbf1", border: "#14b8a6" }, + missing: { fill: "#fee2e2", border: "#ef4444" }, + absent: { fill: "#f3f4f6", border: "#d1d5db" }, +}; + +// Edge type → visual properties (matching pipeline_schema.py EDGE_STYLES) +export const EDGE_STYLES: Record< + string, + { color: string; style: string; width: number } +> = { + data_flow: { color: "#334155", style: "solid", width: 2 }, + produces_artifact: { color: "#16a34a", style: "solid", width: 2 }, + uses_utility: { color: "#7c3aed", style: "dashed", width: 1.5 }, + external_source: { color: "#b45309", style: "dotted", width: 1.5 }, + runs_on_infra: { color: "#dc2626", style: "dashed", width: 1.5 }, + informational: { color: "#9ca3af", style: "dotted", width: 1 }, +}; diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/components/ElkEdge.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/ElkEdge.tsx new file mode 100644 index 000000000..199570eb7 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/ElkEdge.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { EdgeLabelRenderer, getSmoothStepPath, type EdgeProps } from "@xyflow/react"; + +/** + * Custom edge that renders using ELK's computed bend points when available, + * falling back to smoothstep for edges without routes. + */ +export default function ElkEdge({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + style = {}, + markerEnd, + label, + data, +}: EdgeProps) { + let edgePath: string; + let labelX: number; + let labelY: number; + + if (data?.elkRoute) { + const { startPoint, endPoint, bendPoints = [] } = data.elkRoute as { + startPoint: { x: number; y: number }; + endPoint: { x: number; y: number }; + bendPoints?: { x: number; y: number }[]; + }; + const points = [startPoint, ...bendPoints, endPoint]; + + edgePath = points + .map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`) + .join(" "); + + // Label at midpoint of the path + const mid = Math.floor(points.length / 2); + labelX = points[mid].x; + labelY = points[mid].y; + } else { + // Fallback to smoothstep + const [path, lx, ly] = getSmoothStepPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + }); + edgePath = path; + labelX = lx; + labelY = ly; + } + + return ( + <> + + {label && ( + +
+ + {label} + +
+
+ )} + + ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/components/NodeDetailPanel.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/NodeDetailPanel.tsx new file mode 100644 index 000000000..182b308ae --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/NodeDetailPanel.tsx @@ -0,0 +1,113 @@ +"use client"; + +import { NODE_COLORS } from "../colors"; + +const TYPE_LABELS: Record = { + input: "Input data", + output: "Output artifact", + process: "Processing step", + utility: "Utility module", + external: "External service", + us_specific: "US-specific", + uk_specific: "UK-specific", + missing: "Planned", + absent: "Not applicable", +}; + +interface NodeDetailProps { + data: { + label: string; + nodeType: string; + description?: string; + details?: string; + source_file?: string; + } | null; + onClose: () => void; +} + +export default function NodeDetailPanel({ data, onClose }: NodeDetailProps) { + if (!data) return null; + + const colors = NODE_COLORS[data.nodeType] || NODE_COLORS.process; + const typeLabel = TYPE_LABELS[data.nodeType] || data.nodeType; + + return ( +
+ {/* Colored top border */} +
+ +
+
+
+

+ {data.label} +

+ + {typeLabel} + +
+ +
+ + {data.description && ( +

+ {data.description} +

+ )} + + {data.details && ( +
+ {data.details} +
+ )} + + {data.source_file && ( +
+ + + + + + + {data.source_file} + +
+ )} +
+
+ ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineDiagram.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineDiagram.tsx new file mode 100644 index 000000000..196bed6dd --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineDiagram.tsx @@ -0,0 +1,276 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import { + ReactFlow, + Background, + Controls, + MiniMap, + ReactFlowProvider, + useReactFlow, + useNodesState, + useEdgesState, + type Node, + type Edge, + BackgroundVariant, +} from "@xyflow/react"; +import "@xyflow/react/dist/style.css"; +import ELK from "elkjs/lib/elk.bundled.js"; +import { NODE_COLORS, EDGE_STYLES } from "../colors"; +import PipelineNode from "./PipelineNode"; +import ElkEdge from "./ElkEdge"; +import NodeDetailPanel from "./NodeDetailPanel"; + +const elk = new ELK(); +const nodeTypes = { pipeline: PipelineNode }; +const edgeTypes = { elk: ElkEdge }; + +const ELK_OPTIONS: Record = { + "elk.algorithm": "layered", + "elk.direction": "DOWN", + "elk.edgeRouting": "ORTHOGONAL", + "elk.spacing.nodeNode": "60", + "elk.layered.spacing.nodeNodeBetweenLayers": "80", + "elk.layered.spacing.edgeNodeBetweenLayers": "30", + "elk.spacing.edgeEdge": "20", + "elk.spacing.edgeNode": "30", + "elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX", + "elk.layered.crossingMinimization.strategy": "LAYER_SWEEP", +}; + +interface StageData { + id: number; + label: string; + title: string; + description: string; + nodes: any[]; + edges: any[]; +} + +/** + * Estimate node size to match the CSS: + * min-w-[180px] max-w-[280px], px-3 py-2, 12px label + 10px description + */ +function estimateNodeSize(node: any) { + let height = 16; // py-2 top + height += 18; // label line + if (node.description) height += 16; // description line + height += 14; // py-2 bottom + return { width: 240, height: Math.max(58, height) }; +} + +/** + * Assign sourceHandle/targetHandle based on relative node positions. + */ +function assignHandles( + sourcePos: { x: number; y: number }, + targetPos: { x: number; y: number } +) { + const dx = targetPos.x - sourcePos.x; + const dy = targetPos.y - sourcePos.y; + + if (Math.abs(dx) >= Math.abs(dy)) { + return dx >= 0 + ? { sourceHandle: "sr", targetHandle: "tl" } + : { sourceHandle: "sl", targetHandle: "tr" }; + } + return dy >= 0 + ? { sourceHandle: "sb", targetHandle: "tt" } + : { sourceHandle: "st", targetHandle: "tb" }; +} + +async function runElkLayout( + pipelineNodes: any[], + pipelineEdges: any[] +): Promise<{ nodes: Node[]; edges: Edge[] }> { + const graph = { + id: "root", + layoutOptions: ELK_OPTIONS, + children: pipelineNodes.map((n: any) => { + const size = estimateNodeSize(n); + return { id: n.id, width: size.width, height: size.height }; + }), + edges: pipelineEdges.map((e: any, i: number) => ({ + id: `e-${i}`, + sources: [e.source], + targets: [e.target], + })), + }; + + const result = await elk.layout(graph); + + // Build position map + const positionMap: Record = {}; + for (const child of result.children || []) { + positionMap[child.id] = { x: child.x || 0, y: child.y || 0 }; + } + + // Build edge route map from ELK sections + const routeMap: Record = {}; + for (const edge of (result.edges || []) as any[]) { + if (edge.sections && edge.sections.length > 0) { + routeMap[edge.id] = edge.sections[0]; + } + } + + // Position nodes + const nodes: Node[] = pipelineNodes.map((n: any) => ({ + id: n.id, + type: "pipeline", + position: positionMap[n.id] || { x: 0, y: 0 }, + data: { + label: n.label || n.id, + nodeType: n.node_type || "process", + description: n.description || "", + details: n.details || "", + source_file: n.source_file || "", + }, + })); + + // Enrich edges with handles + ELK routes + const edges: Edge[] = pipelineEdges.map((e: any, i: number) => { + const edgeId = `e-${i}`; + const srcPos = positionMap[e.source] || { x: 0, y: 0 }; + const tgtPos = positionMap[e.target] || { x: 0, y: 0 }; + const handles = assignHandles(srcPos, tgtPos); + const route = routeMap[edgeId]; + const edgeStyle = EDGE_STYLES[e.edge_type] || EDGE_STYLES.data_flow; + + return { + id: edgeId, + source: e.source, + target: e.target, + ...handles, + type: "elk", + label: e.label || undefined, + data: { + elkRoute: route || null, + }, + style: { + stroke: edgeStyle.color, + strokeWidth: edgeStyle.width, + strokeDasharray: + edgeStyle.style === "dashed" ? "6 3" : edgeStyle.style === "dotted" ? "2 2" : undefined, + }, + }; + }); + + return { nodes, edges }; +} + +function DiagramInner({ stage }: { stage: StageData }) { + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [selectedNode, setSelectedNode] = useState(null); + const [layoutDone, setLayoutDone] = useState(false); + const { fitView } = useReactFlow(); + const hasFit = useRef(false); + + useEffect(() => { + if (!stage?.nodes?.length) return; + hasFit.current = false; + setLayoutDone(false); + runElkLayout(stage.nodes, stage.edges).then(({ nodes, edges }) => { + setNodes(nodes); + setEdges(edges); + setLayoutDone(true); + }); + }, [stage]); + + useEffect(() => { + if (layoutDone && !hasFit.current) { + hasFit.current = true; + requestAnimationFrame(() => { + fitView({ padding: 0.15 }); + }); + } + }, [layoutDone, fitView]); + + const onNodeClick = useCallback((_: any, node: Node) => { + setSelectedNode(node.data); + }, []); + + const onPaneClick = useCallback(() => { + setSelectedNode(null); + }, []); + + if (!stage?.nodes?.length) { + return ( +
+
+
+ No diagram data available +
+
+
+ ); + } + + return ( +
+ {/* Stage info */} +
+

+ {stage.title} +

+

+ {stage.description} +

+
+ + {stage.nodes.length} nodes + + · + + {stage.edges.length} edges + +
+
+ + + + + NODE_COLORS[n.data?.nodeType as string]?.border || "#9CA3AF"} + maskColor="rgba(0,0,0,0.04)" + style={{ width: 140, height: 90 }} + /> + + + setSelectedNode(null)} /> +
+ ); +} + +export default function PipelineDiagram({ stage }: { stage: StageData }) { + return ( + + + + ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineNode.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineNode.tsx new file mode 100644 index 000000000..21d3bb7ed --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/PipelineNode.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { Handle, Position } from "@xyflow/react"; +import { NODE_COLORS } from "../colors"; + +const handleClass = "!w-2 !h-2 !border-none"; + +/** + * 8 handles — source + target on each of 4 sides. + * Handle ID convention: + * "sr" = source-right, "tl" = target-left (horizontal flow →) + * "sb" = source-bottom, "tt" = target-top (vertical flow ↓) + * "st" = source-top, "tb" = target-bottom (upward flow ↑) + * "sl" = source-left, "tr" = target-right (leftward flow ←) + */ +function AllHandles({ color }: { color: string }) { + const s = { background: color }; + return ( + <> + + + + + + + + + + ); +} + +export default function PipelineNode({ data }: { data: any }) { + const colors = NODE_COLORS[data.nodeType] || NODE_COLORS.process; + const isDashed = data.nodeType === "missing" || data.nodeType === "absent"; + + return ( +
+
+ {data.label} +
+ {data.description && ( +
+ {data.description} +
+ )} + +
+ ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/components/Sidebar.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/Sidebar.tsx new file mode 100644 index 000000000..91df39c76 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/components/Sidebar.tsx @@ -0,0 +1,173 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { NODE_COLORS, EDGE_STYLES } from "../colors"; +import pipelineData from "../pipeline.json"; + +const NODE_LEGEND = [ + { type: "input", label: "Input" }, + { type: "output", label: "Output" }, + { type: "process", label: "Process" }, + { type: "utility", label: "Utility" }, + { type: "external", label: "External" }, + { type: "us_specific", label: "US-specific" }, + { type: "missing", label: "Missing" }, + { type: "absent", label: "Absent" }, +]; + +const EDGE_LEGEND = [ + { type: "data_flow", label: "Data flow" }, + { type: "produces_artifact", label: "Produces artifact" }, + { type: "uses_utility", label: "Uses utility" }, + { type: "external_source", label: "External source" }, + { type: "runs_on_infra", label: "Runs on infra" }, + { type: "informational", label: "Informational" }, +]; + +export default function Sidebar() { + const pathname = usePathname(); + const country = "us"; + const stages = pipelineData.stages; + + return ( + + ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/favicon.ico b/docs/pipeline-diagrams/demos/pipeline-demos/app/favicon.ico new file mode 100644 index 000000000..718d6fea4 Binary files /dev/null and b/docs/pipeline-diagrams/demos/pipeline-demos/app/favicon.ico differ diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/globals.css b/docs/pipeline-diagrams/demos/pipeline-demos/app/globals.css new file mode 100644 index 000000000..86a0922c0 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/globals.css @@ -0,0 +1,103 @@ +@import "tailwindcss"; + +@theme inline { + --color-pe-primary-50: #E6FFFA; + --color-pe-primary-400: #38B2AC; + --color-pe-primary-500: #319795; + --color-pe-primary-600: #2C7A7B; + --color-pe-primary-700: #285E61; + --color-pe-primary-800: #234E52; + --color-pe-gray-50: #F9FAFB; + --color-pe-gray-100: #F2F4F7; + --color-pe-gray-200: #E2E8F0; + --color-pe-gray-500: #6B7280; + --color-pe-gray-600: #4B5563; + --color-pe-gray-700: #344054; +} + +:root { + /* PolicyEngine design tokens */ + --pe-primary-50: #E6FFFA; + --pe-primary-400: #38B2AC; + --pe-primary-500: #319795; + --pe-primary-600: #2C7A7B; + --pe-primary-700: #285E61; + --pe-primary-800: #234E52; + + --pe-gray-50: #F9FAFB; + --pe-gray-100: #F2F4F7; + --pe-gray-200: #E2E8F0; + --pe-gray-500: #6B7280; + --pe-gray-600: #4B5563; + --pe-gray-700: #344054; + + --pe-text-primary: #000000; + --pe-text-secondary: #5A5A5A; + --pe-text-tertiary: #9CA3AF; + + --pe-bg-primary: #FFFFFF; + --pe-bg-secondary: #F5F9FF; + --pe-bg-tertiary: #F1F5F9; + + --pe-blue-500: #0EA5E9; + --pe-success: #22C55E; + --pe-error: #EF4444; + --pe-warning: #FEC601; + + --pe-font-primary: 'Inter', system-ui, sans-serif; + --pe-font-mono: 'JetBrains Mono', monospace; + + --pe-radius-sm: 4px; + --pe-radius-md: 6px; + --pe-radius-lg: 8px; +} + +* { box-sizing: border-box; } + +body { + background: var(--pe-bg-primary); + color: var(--pe-text-primary); + font-family: var(--pe-font-primary); + -webkit-font-smoothing: antialiased; +} + +/* React Flow overrides */ +.react-flow__background { + background-color: var(--pe-bg-secondary) !important; +} + +.react-flow__controls { + box-shadow: 0 1px 3px rgba(0,0,0,0.06) !important; + border: 1px solid var(--pe-gray-200) !important; + border-radius: var(--pe-radius-lg) !important; + overflow: hidden !important; +} + +.react-flow__controls-button { + border-bottom: 1px solid var(--pe-gray-100) !important; + background: white !important; + width: 32px !important; + height: 32px !important; +} + +.react-flow__controls-button:hover { + background: var(--pe-gray-50) !important; +} + +.react-flow__minimap { + border-radius: var(--pe-radius-lg) !important; + box-shadow: 0 1px 3px rgba(0,0,0,0.06) !important; + border: 1px solid var(--pe-gray-200) !important; + background: white !important; +} + +.react-flow__edge-text { + font-family: var(--pe-font-primary) !important; + font-size: 10px !important; +} + +/* Sidebar scrollbar */ +aside::-webkit-scrollbar { width: 3px; } +aside::-webkit-scrollbar-track { background: transparent; } +aside::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.08); border-radius: 3px; } +aside::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.15); } diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/layout.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/layout.tsx new file mode 100644 index 000000000..e1769cd7a --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/layout.tsx @@ -0,0 +1,29 @@ +import type { Metadata } from "next"; +import "./globals.css"; +import Sidebar from "./components/Sidebar"; + +export const metadata: Metadata = { + title: "Pipeline explorer — PolicyEngine US Data", + description: "Interactive pipeline documentation for the US data build process", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + + +
{children}
+ + + ); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/page.tsx b/docs/pipeline-diagrams/demos/pipeline-demos/app/page.tsx new file mode 100644 index 000000000..8da13a42f --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Home() { + redirect("/us"); +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/app/pipeline.json b/docs/pipeline-diagrams/demos/pipeline-demos/app/pipeline.json new file mode 100644 index 000000000..df31f401a --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/app/pipeline.json @@ -0,0 +1,1922 @@ +{ + "stages": [ + { + "id": 0, + "label": "Stage 0", + "title": "Stage 0: Raw Data Download", + "description": "Download raw survey data from Census, IRS, Federal Reserve, and HuggingFace", + "country": "us", + "nodes": [ + { + "id": "cps_url", + "label": "Census CPS ASEC", + "node_type": "input", + "description": "ZIP with CSV files from www2.census.gov" + }, + { + "id": "acs_url", + "label": "Census ACS PUMS", + "node_type": "input", + "description": "Person + Household CSVs, 1-Year estimates" + }, + { + "id": "scf_url", + "label": "Federal Reserve SCF", + "node_type": "input", + "description": "Stata format (.dta), Survey of Consumer Finances" + }, + { + "id": "hf_private", + "label": "HuggingFace Private Repo", + "node_type": "external", + "description": "policyengine/irs-soi-puf \u2014 requires HUGGING_FACE_TOKEN" + }, + { + "id": "hf_public", + "label": "HuggingFace Public Repo", + "node_type": "external", + "description": "policyengine/policyengine-us-data \u2014 SIPP, block distributions, policy_data.db" + }, + { + "id": "download_http", + "label": "HTTP Download + ZIP Extract", + "node_type": "process", + "description": "requests.get() with streaming, tqdm progress bar" + }, + { + "id": "download_hf", + "label": "HuggingFace Hub Download", + "node_type": "process", + "description": "hf_hub_download(), token-authenticated for private repos" + }, + { + "id": "csv_parse", + "label": "CSV/Stata Parsing", + "node_type": "process", + "description": "pandas read_csv/read_stata, entity table construction" + }, + { + "id": "out_cps_raw", + "label": "census_cps_2024.h5", + "node_type": "output", + "description": "5 entity tables: Person, Family, Household, Tax Unit, SPM Unit" + }, + { + "id": "out_acs_raw", + "label": "census_acs_2022.h5", + "node_type": "output", + "description": "Person + Household tables" + }, + { + "id": "out_puf_raw", + "label": "irs_puf_2015.h5", + "node_type": "output", + "description": "PUF + Demographics tables" + }, + { + "id": "out_soi", + "label": "soi.csv", + "node_type": "output", + "description": "SOI aggregate statistics" + }, + { + "id": "out_scf", + "label": "SCF raw data", + "node_type": "output", + "description": "Stata \u2192 DataFrame, auto loans + net worth" + }, + { + "id": "out_sipp", + "label": "pu2023_slim.csv", + "node_type": "output", + "description": "SIPP microdata (pipe-delimited)" + }, + { + "id": "out_block", + "label": "block_cd_distributions.csv.gz", + "node_type": "output", + "description": "Census block populations, block-to-CD crosswalk" + }, + { + "id": "out_pop", + "label": "np2023_d5_mid.csv", + "node_type": "output", + "description": "Census population projections" + }, + { + "id": "out_calibration_db", + "label": "policy_data.db", + "node_type": "output", + "description": "SQLite calibration targets database" + }, + { + "id": "util_storage", + "label": "STORAGE_FOLDER", + "node_type": "utility", + "description": "policyengine_us_data/storage/ \u2014 all downloads cached here" + } + ], + "edges": [ + { + "source": "cps_url", + "target": "download_http", + "edge_type": "external_source", + "label": "CPS ASEC ZIP" + }, + { + "source": "acs_url", + "target": "download_http", + "edge_type": "external_source", + "label": "ACS PUMS CSV" + }, + { + "source": "scf_url", + "target": "download_http", + "edge_type": "external_source", + "label": "SCF .dta" + }, + { + "source": "hf_private", + "target": "download_hf", + "edge_type": "external_source", + "label": "PUF, demographics, SOI, pop" + }, + { + "source": "hf_public", + "target": "download_hf", + "edge_type": "external_source", + "label": "SIPP, block, policy_data.db" + }, + { + "source": "download_http", + "target": "csv_parse", + "edge_type": "data_flow", + "label": "raw files" + }, + { + "source": "download_hf", + "target": "csv_parse", + "edge_type": "data_flow", + "label": "raw files" + }, + { + "source": "csv_parse", + "target": "out_cps_raw", + "edge_type": "produces_artifact", + "label": "census_cps_2024.h5" + }, + { + "source": "csv_parse", + "target": "out_acs_raw", + "edge_type": "produces_artifact", + "label": "census_acs_2022.h5" + }, + { + "source": "csv_parse", + "target": "out_puf_raw", + "edge_type": "produces_artifact", + "label": "irs_puf_2015.h5" + }, + { + "source": "csv_parse", + "target": "out_soi", + "edge_type": "produces_artifact", + "label": "soi.csv" + }, + { + "source": "download_http", + "target": "out_scf", + "edge_type": "produces_artifact", + "label": "SCF raw data" + }, + { + "source": "download_hf", + "target": "out_sipp", + "edge_type": "produces_artifact", + "label": "pu2023_slim.csv" + }, + { + "source": "download_hf", + "target": "out_block", + "edge_type": "produces_artifact", + "label": "block_cd_distributions.csv.gz" + }, + { + "source": "download_hf", + "target": "out_pop", + "edge_type": "produces_artifact", + "label": "np2023_d5_mid.csv" + }, + { + "source": "download_hf", + "target": "out_calibration_db", + "edge_type": "produces_artifact", + "label": "policy_data.db" + } + ] + }, + { + "id": 1, + "label": "Stage 1", + "title": "Stage 1: Base Dataset Construction", + "description": "Build CPS 2024 and PUF 2024 from raw survey data", + "country": "us", + "nodes": [ + { + "id": "in_census_cps", + "label": "census_cps_2024.h5", + "node_type": "input", + "description": "Raw CPS ASEC from Stage 0" + }, + { + "id": "in_census_cps_prev", + "label": "census_cps_2023.h5", + "node_type": "input", + "description": "Previous year CPS for income matching" + }, + { + "id": "in_acs", + "label": "ACS 2022", + "node_type": "input", + "description": "Training data for rent QRF" + }, + { + "id": "in_sipp", + "label": "SIPP 2023", + "node_type": "input", + "description": "Training data for tips QRF" + }, + { + "id": "in_scf", + "label": "SCF 2022", + "node_type": "input", + "description": "Training data for auto loans QRF" + }, + { + "id": "in_uprating", + "label": "uprating_factors.csv", + "node_type": "input", + "description": "PE uprating factors table" + }, + { + "id": "out_cps", + "label": "cps_2024.h5", + "node_type": "output", + "description": "Dataset.ARRAYS format, ~65K households (half-sample)" + }, + { + "id": "out_puf", + "label": "puf_2024.h5", + "node_type": "output", + "description": "Dataset.ARRAYS format" + }, + { + "id": "in_irs_puf", + "label": "irs_puf_2015.h5", + "node_type": "input", + "description": "Raw IRS PUF from Stage 0" + }, + { + "id": "in_demographics", + "label": "demographics_2015.csv", + "node_type": "input", + "description": "PUF demographics from HuggingFace" + }, + { + "id": "util_seeded_rng", + "label": "seeded_rng()", + "node_type": "utility", + "description": "Deterministic per-variable RNG" + }, + { + "id": "util_qrf", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + }, + { + "id": "util_retirement_limits", + "label": "get_retirement_limits()", + "node_type": "utility", + "description": "IRS contribution limits" + }, + { + "id": "add_rent", + "label": "Rent Imputation (QRF)", + "node_type": "process", + "description": "Impute rent and real estate taxes using QRF from ACS 2022", + "details": "10K sampled household heads as training data", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "impute_puf_demographics", + "label": "Impute PUF Demographics", + "node_type": "process", + "description": "QRF imputation for age, gender, and earnings split", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_ssn_card_type", + "label": "SSN Card Type", + "node_type": "us_specific", + "description": "US immigration classification from 14 ASEC conditions", + "details": "Undocumented target: 13M; SSN card types 0-3", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_personal_variables", + "label": "Add Personal Variables", + "node_type": "process", + "description": "Age, sex, disability, occupation, overtime flags", + "details": "80+ ages randomized to 80-84; 12 overtime occupation flags", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_id_variables", + "label": "Add ID Variables", + "node_type": "process", + "description": "Create person_id, household_id, tax_unit_id, spm_unit_id, marital_unit_id", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_tips", + "label": "Tips Imputation (QRF)", + "node_type": "process", + "description": "Impute tip income and liquid assets from SIPP 2023", + "details": "Models cached as pickle; QRF fit_predict()", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_auto_loan", + "label": "Auto Loan / Net Worth (QRF)", + "node_type": "process", + "description": "Impute auto loan balance, interest, and net worth from SCF 2022", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "simulate_qbi", + "label": "QBI Simulation", + "node_type": "process", + "description": "Simulate W-2 wages, UBIA, and SSTB for Section 199A", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_spm_variables", + "label": "SPM Variables", + "node_type": "process", + "description": "SPM thresholds and transfers (SNAP, housing, energy subsidies)", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "preprocess_puf", + "label": "Preprocess PUF", + "node_type": "process", + "description": "Rename 60+ IRS variables to PolicyEngine names", + "details": "E00200 \u2192 employment_income, etc.", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_personal_income_variables", + "label": "Add Income Variables", + "node_type": "process", + "description": "30+ income types with splits", + "details": "SS classified by reason codes; retirement split by account type; capital gains 88% LT / 12% ST", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_previous_year_income", + "label": "Previous Year Income", + "node_type": "process", + "description": "Cross-year PERIDNUM linking for prior-year income", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_household_variables", + "label": "Household Variables", + "node_type": "process", + "description": "State FIPS, county FIPS, NYC flag from county codes", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_takeup", + "label": "Benefit Takeup", + "node_type": "us_specific", + "description": "Stochastic takeup for 9 benefit programs", + "details": "SNAP, ACA, Medicaid, EITC, SSI, TANF, WIC, Head Start; state-specific Medicaid rates", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "downsample", + "label": "Downsampling", + "node_type": "process", + "description": "frac=0.5 for CPS_2024 using Microsimulation.subsample()", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + } + ], + "edges": [ + { + "source": "in_census_cps", + "target": "add_id_variables", + "edge_type": "data_flow", + "label": "raw CPS tables" + }, + { + "source": "add_id_variables", + "target": "add_personal_variables", + "edge_type": "data_flow" + }, + { + "source": "add_personal_variables", + "target": "add_personal_income_variables", + "edge_type": "data_flow" + }, + { + "source": "add_personal_income_variables", + "target": "add_previous_year_income", + "edge_type": "data_flow" + }, + { + "source": "in_census_cps_prev", + "target": "add_previous_year_income", + "edge_type": "data_flow", + "label": "prior year PERIDNUM" + }, + { + "source": "add_previous_year_income", + "target": "add_ssn_card_type", + "edge_type": "data_flow" + }, + { + "source": "add_ssn_card_type", + "target": "add_spm_variables", + "edge_type": "data_flow" + }, + { + "source": "add_spm_variables", + "target": "add_household_variables", + "edge_type": "data_flow" + }, + { + "source": "add_household_variables", + "target": "add_rent", + "edge_type": "data_flow" + }, + { + "source": "in_acs", + "target": "add_rent", + "edge_type": "external_source", + "label": "ACS training data" + }, + { + "source": "add_rent", + "target": "add_tips", + "edge_type": "data_flow" + }, + { + "source": "in_sipp", + "target": "add_tips", + "edge_type": "external_source", + "label": "SIPP training data" + }, + { + "source": "add_tips", + "target": "add_auto_loan", + "edge_type": "data_flow" + }, + { + "source": "in_scf", + "target": "add_auto_loan", + "edge_type": "external_source", + "label": "SCF training data" + }, + { + "source": "add_auto_loan", + "target": "add_takeup", + "edge_type": "data_flow" + }, + { + "source": "add_takeup", + "target": "downsample", + "edge_type": "data_flow" + }, + { + "source": "downsample", + "target": "out_cps", + "edge_type": "produces_artifact", + "label": "cps_2024.h5" + }, + { + "source": "in_irs_puf", + "target": "preprocess_puf", + "edge_type": "data_flow", + "label": "raw PUF records" + }, + { + "source": "preprocess_puf", + "target": "simulate_qbi", + "edge_type": "data_flow" + }, + { + "source": "simulate_qbi", + "target": "impute_puf_demographics", + "edge_type": "data_flow" + }, + { + "source": "in_demographics", + "target": "impute_puf_demographics", + "edge_type": "data_flow", + "label": "demographics_2015.csv" + }, + { + "source": "impute_puf_demographics", + "target": "out_puf", + "edge_type": "produces_artifact", + "label": "puf_2024.h5" + }, + { + "source": "in_uprating", + "target": "out_puf", + "edge_type": "data_flow", + "label": "SOI growth rates" + }, + { + "source": "util_seeded_rng", + "target": "add_takeup", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_rent", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_tips", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_auto_loan", + "edge_type": "uses_utility" + }, + { + "source": "util_retirement_limits", + "target": "add_personal_income_variables", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "impute_puf_demographics", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 2, + "label": "Stage 2", + "title": "Stage 2: Extended CPS (PUF Clone)", + "description": "Merge CPS + PUF via cloning, impute 64 income vars + 51 override vars via QRF", + "country": "us", + "nodes": [ + { + "id": "in_cps_s2", + "label": "CPS_2024_Full", + "node_type": "input", + "description": "From Stage 1 (full sample)" + }, + { + "id": "in_puf_s2", + "label": "PUF_2024", + "node_type": "input", + "description": "From Stage 1" + }, + { + "id": "in_blocks_s2", + "label": "block_cd_distributions.csv.gz", + "node_type": "input", + "description": "Census block populations" + }, + { + "id": "geo_assign_s2", + "label": "Geography Assignment", + "node_type": "process", + "description": "assign_random_geography() \u2014 population-weighted block draw" + }, + { + "id": "record_double", + "label": "Record Doubling", + "node_type": "process", + "description": "puf_clone_dataset() \u2014 CPS half keeps originals, PUF half starts with zero weight" + }, + { + "id": "qrf_pass1", + "label": "QRF Pass 1: Full Imputation", + "node_type": "process", + "description": "64 income variables \u2014 training on PUF ~20K records, 7 demographic predictors" + }, + { + "id": "qrf_pass2", + "label": "QRF Pass 2: Override Imputation", + "node_type": "process", + "description": "51 variables (both halves) \u2014 partnership, S-corp, charitable, mortgage, credits" + }, + { + "id": "retire_impute", + "label": "Retirement Contribution Imputation", + "node_type": "process", + "description": "401k, IRA, SE pension \u2014 IRS limits + catch-up applied" + }, + { + "id": "weeks_impute", + "label": "Weeks Unemployed Imputation", + "node_type": "process", + "description": "QRF on CPS weeks_unemployed \u2014 clips [0, 52], zero if no UC" + }, + { + "id": "ss_reconcile", + "label": "SS Sub-component Reconciliation", + "node_type": "process", + "description": "Retirement/Disability/Survivors/Dependents \u2014 scaled to match PUF total" + }, + { + "id": "cps_only", + "label": "CPS-Only Variable Re-imputation", + "node_type": "process", + "description": "80 variables for PUF half \u2014 transfers, SPM, medical, hours, retirement" + }, + { + "id": "formula_drop", + "label": "Formula Variable Dropping", + "node_type": "process", + "description": "Remove PE-computed variables, rename employment_income \u2192 employment_income_before_lsr" + }, + { + "id": "out_ext", + "label": "extended_cps_2024.h5", + "node_type": "output", + "description": "~260K households (doubled), CPS half + PUF half" + }, + { + "id": "util_puf_clone", + "label": "puf_clone_dataset()", + "node_type": "utility", + "description": "calibration/puf_impute.py" + }, + { + "id": "util_qrf_s2", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + } + ], + "edges": [ + { + "source": "in_cps_s2", + "target": "geo_assign_s2", + "edge_type": "data_flow", + "label": "CPS records" + }, + { + "source": "in_blocks_s2", + "target": "geo_assign_s2", + "edge_type": "data_flow", + "label": "block populations" + }, + { + "source": "in_puf_s2", + "target": "record_double", + "edge_type": "data_flow", + "label": "PUF records" + }, + { + "source": "in_cps_s2", + "target": "record_double", + "edge_type": "data_flow", + "label": "CPS records" + }, + { + "source": "geo_assign_s2", + "target": "record_double", + "edge_type": "data_flow" + }, + { + "source": "record_double", + "target": "qrf_pass1", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass1", + "target": "qrf_pass2", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass2", + "target": "retire_impute", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass2", + "target": "weeks_impute", + "edge_type": "data_flow" + }, + { + "source": "retire_impute", + "target": "ss_reconcile", + "edge_type": "data_flow" + }, + { + "source": "weeks_impute", + "target": "ss_reconcile", + "edge_type": "data_flow" + }, + { + "source": "ss_reconcile", + "target": "cps_only", + "edge_type": "data_flow" + }, + { + "source": "cps_only", + "target": "formula_drop", + "edge_type": "data_flow" + }, + { + "source": "formula_drop", + "target": "out_ext", + "edge_type": "produces_artifact" + }, + { + "source": "util_puf_clone", + "target": "record_double", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "qrf_pass1", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "qrf_pass2", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "cps_only", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 3, + "label": "Stage 3", + "title": "Stage 3: Stratified CPS + Enhanced CPS Reweighting", + "description": "Stratify by income (keep top 1%), reweight to match calibration targets", + "country": "us", + "nodes": [ + { + "id": "in_ext_cps", + "label": "extended_cps_2024.h5", + "node_type": "input", + "description": "From Stage 2 (~260K HH)" + }, + { + "id": "calc_agi", + "label": "Calculate AGI", + "node_type": "process", + "description": "Microsimulation.calculate() \u2014 adjusted_gross_income mapped to household" + }, + { + "id": "strat_top", + "label": "Retain Top 1% by AGI", + "node_type": "process", + "description": "All high-income households kept \u2014 preserves tail representation" + }, + { + "id": "strat_sample", + "label": "Uniform Sample Remaining 99%", + "node_type": "process", + "description": "Target: ~12,000 households \u2014 optional 1.5x oversample of bottom 25%" + }, + { + "id": "out_strat", + "label": "stratified_extended_cps_2024.h5", + "node_type": "output", + "description": "~12K households \u2014 input to Stages 4-6" + }, + { + "id": "in_ext_half", + "label": "ExtendedCPS_2024_Half", + "node_type": "input", + "description": "Half-sample from Stage 2" + }, + { + "id": "build_loss", + "label": "build_loss_matrix()", + "node_type": "process", + "description": "Hundreds of calibration targets \u2014 returns (matrix, target_vector)" + }, + { + "id": "t_soi", + "label": "IRS SOI", + "node_type": "external", + "description": "AGI, income types, filer counts by AGI band / filing status" + }, + { + "id": "t_census", + "label": "Census Population", + "node_type": "external", + "description": "86 single-year age groups from np2023_d5_mid.csv" + }, + { + "id": "t_cbo", + "label": "CBO Budget", + "node_type": "external", + "description": "Income tax, SNAP, SS, SSI, UC" + }, + { + "id": "t_state", + "label": "State Targets", + "node_type": "us_specific", + "description": "50 states + DC \u2014 population, AGI, ACA, SNAP, age groups" + }, + { + "id": "t_jct", + "label": "JCT Tax Expenditures", + "node_type": "external", + "description": "SALT, medical, charitable \u2014 counterfactual simulations" + }, + { + "id": "reweight", + "label": "reweight()", + "node_type": "process", + "description": "PyTorch Adam + L0 HardConcrete \u2014 500 epochs, lr=0.2" + }, + { + "id": "weight_validate", + "label": "Weight Validation", + "node_type": "process", + "description": "No NaN, no negatives \u2014 100M < total HH < 200M" + }, + { + "id": "out_enhanced", + "label": "enhanced_cps_2024.h5", + "node_type": "output", + "description": "Reweighted production simulation dataset" + }, + { + "id": "util_loss", + "label": "build_loss_matrix()", + "node_type": "utility", + "description": "utils/loss.py" + }, + { + "id": "util_l0_s3", + "label": "HardConcrete L0", + "node_type": "utility", + "description": "utils/l0.py" + } + ], + "edges": [ + { + "source": "in_ext_cps", + "target": "calc_agi", + "edge_type": "data_flow" + }, + { + "source": "calc_agi", + "target": "strat_top", + "edge_type": "data_flow" + }, + { + "source": "strat_top", + "target": "strat_sample", + "edge_type": "data_flow" + }, + { + "source": "strat_top", + "target": "out_strat", + "edge_type": "data_flow", + "label": "top 1%" + }, + { + "source": "strat_sample", + "target": "out_strat", + "edge_type": "data_flow", + "label": "sampled 99%" + }, + { + "source": "in_ext_half", + "target": "build_loss", + "edge_type": "data_flow" + }, + { + "source": "t_soi", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_census", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_cbo", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_jct", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_state", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "build_loss", + "target": "reweight", + "edge_type": "data_flow", + "label": "(matrix, targets)" + }, + { + "source": "reweight", + "target": "weight_validate", + "edge_type": "data_flow" + }, + { + "source": "weight_validate", + "target": "out_enhanced", + "edge_type": "produces_artifact" + }, + { + "source": "util_loss", + "target": "build_loss", + "edge_type": "uses_utility" + }, + { + "source": "util_l0_s3", + "target": "reweight", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 4, + "label": "Stage 4", + "title": "Stage 4: Source Imputation (ACS + SIPP + SCF)", + "description": "Impute wealth/assets from external surveys onto stratified CPS via QRF", + "country": "us", + "nodes": [ + { + "id": "in_strat_s4", + "label": "stratified_extended_cps_2024.h5", + "node_type": "input", + "description": "From Stage 3 (~12K HH)" + }, + { + "id": "in_acs_s4", + "label": "ACS_2022", + "node_type": "input", + "description": "American Community Survey \u2014 has state_fips predictor" + }, + { + "id": "in_sipp_s4", + "label": "SIPP 2023", + "node_type": "external", + "description": "pu2023_slim.csv from HuggingFace" + }, + { + "id": "in_scf_s4", + "label": "SCF_2022", + "node_type": "input", + "description": "Survey of Consumer Finances \u2014 50% random subsample" + }, + { + "id": "geo_assign_s4", + "label": "Geography Assignment", + "node_type": "process", + "description": "assign_random_geography(n_clones=1) \u2014 block draw + state FIPS" + }, + { + "id": "acs_qrf", + "label": "ACS QRF Imputation", + "node_type": "process", + "description": "10 predictors incl. state_fips \u2014 imputes rent, real_estate_taxes" + }, + { + "id": "sipp_tips_qrf", + "label": "SIPP Tips QRF", + "node_type": "process", + "description": "4 predictors, NO state \u2014 imputes tip_income" + }, + { + "id": "sipp_assets_qrf", + "label": "SIPP Assets QRF", + "node_type": "process", + "description": "5 predictors \u2014 imputes bank_account_assets, stock_assets, bond_assets" + }, + { + "id": "scf_qrf", + "label": "SCF QRF Imputation", + "node_type": "process", + "description": "8 predictors \u2014 imputes net_worth, auto_loan_balance, auto_loan_interest" + }, + { + "id": "out_imputed", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "output", + "description": "Enriched with ACS/SIPP/SCF vars \u2014 uploaded to HuggingFace" + }, + { + "id": "util_clone_assign", + "label": "clone_and_assign.py", + "node_type": "utility", + "description": "Geography assignment" + }, + { + "id": "util_qrf_s4", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + } + ], + "edges": [ + { + "source": "in_strat_s4", + "target": "geo_assign_s4", + "edge_type": "data_flow" + }, + { + "source": "geo_assign_s4", + "target": "acs_qrf", + "edge_type": "data_flow", + "label": "state_fips" + }, + { + "source": "in_acs_s4", + "target": "acs_qrf", + "edge_type": "data_flow" + }, + { + "source": "in_sipp_s4", + "target": "sipp_tips_qrf", + "edge_type": "external_source" + }, + { + "source": "in_sipp_s4", + "target": "sipp_assets_qrf", + "edge_type": "external_source" + }, + { + "source": "in_scf_s4", + "target": "scf_qrf", + "edge_type": "external_source" + }, + { + "source": "acs_qrf", + "target": "sipp_tips_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "sipp_tips_qrf", + "target": "sipp_assets_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "sipp_assets_qrf", + "target": "scf_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "scf_qrf", + "target": "out_imputed", + "edge_type": "produces_artifact" + }, + { + "source": "util_clone_assign", + "target": "geo_assign_s4", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "acs_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "sipp_tips_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "sipp_assets_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "scf_qrf", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 5, + "label": "Stage 5", + "title": "Stage 5: Matrix Build (Calibration Target Construction)", + "description": "Build sparse calibration matrix (targets \u00d7 households \u00d7 clones)", + "country": "us", + "nodes": [ + { + "id": "in_cps_s5", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "input", + "description": "From Stage 4 (~12K HH)" + }, + { + "id": "in_db_s5", + "label": "policy_data.db", + "node_type": "external", + "description": "SQLite calibration database \u2014 10-step ETL" + }, + { + "id": "in_config_s5", + "label": "target_config.yaml", + "node_type": "input", + "description": "Active target include list" + }, + { + "id": "in_blocks_s5", + "label": "block_cd_distributions.csv.gz", + "node_type": "input", + "description": "Census block populations" + }, + { + "id": "target_resolve", + "label": "Target Resolution", + "node_type": "process", + "description": "SQL query to target_overview \u2014 ~8,000 rows total" + }, + { + "id": "target_uprate", + "label": "Target Uprating", + "node_type": "process", + "description": "CPI-U for dollars, pop growth for counts" + }, + { + "id": "geo_build", + "label": "Geography Index Build", + "node_type": "process", + "description": "state_to_cols, cd_to_cols maps" + }, + { + "id": "constraint_resolve", + "label": "Constraint Resolution", + "node_type": "process", + "description": "Non-geographic constraints from DB (age, medicaid, filer)" + }, + { + "id": "state_precomp", + "label": "Per-State Precomputation", + "node_type": "us_specific", + "description": "51 fresh Microsimulations \u2014 most expensive step" + }, + { + "id": "clone_assembly", + "label": "Clone Assembly", + "node_type": "process", + "description": "430 clones \u00d7 12K records = 5.16M columns" + }, + { + "id": "takeup_rerand", + "label": "Block-Level Takeup Re-randomization", + "node_type": "process", + "description": "Seeded on (block_geoid, hh_id) \u2014 ensures calibration consistency" + }, + { + "id": "sparse_build", + "label": "Sparse Matrix Construction", + "node_type": "process", + "description": "COO triples \u2192 CSR matrix \u2014 shape (n_targets, 5.16M), ~0.02% nonzero" + }, + { + "id": "out_pkg", + "label": "calibration_package.pkl", + "node_type": "output", + "description": "X_sparse CSR matrix, targets_df, initial_weights, metadata" + }, + { + "id": "util_sql", + "label": "sqlalchemy", + "node_type": "utility", + "description": "Database queries" + }, + { + "id": "util_pool", + "label": "ProcessPoolExecutor", + "node_type": "utility", + "description": "Parallel state computation" + }, + { + "id": "util_takeup_s5", + "label": "compute_block_takeup_for_entities()", + "node_type": "utility", + "description": "utils/takeup.py" + }, + { + "id": "util_scipy", + "label": "scipy.sparse", + "node_type": "utility", + "description": "CSR/COO matrix construction" + } + ], + "edges": [ + { + "source": "in_cps_s5", + "target": "target_resolve", + "edge_type": "data_flow" + }, + { + "source": "in_db_s5", + "target": "target_resolve", + "edge_type": "external_source", + "label": "SQL targets" + }, + { + "source": "in_config_s5", + "target": "target_resolve", + "edge_type": "data_flow", + "label": "include list" + }, + { + "source": "target_resolve", + "target": "target_uprate", + "edge_type": "data_flow" + }, + { + "source": "target_uprate", + "target": "geo_build", + "edge_type": "data_flow" + }, + { + "source": "geo_build", + "target": "constraint_resolve", + "edge_type": "data_flow" + }, + { + "source": "constraint_resolve", + "target": "state_precomp", + "edge_type": "data_flow" + }, + { + "source": "in_cps_s5", + "target": "state_precomp", + "edge_type": "data_flow", + "label": "household data" + }, + { + "source": "state_precomp", + "target": "clone_assembly", + "edge_type": "data_flow" + }, + { + "source": "in_blocks_s5", + "target": "clone_assembly", + "edge_type": "data_flow", + "label": "block populations" + }, + { + "source": "clone_assembly", + "target": "takeup_rerand", + "edge_type": "data_flow" + }, + { + "source": "takeup_rerand", + "target": "sparse_build", + "edge_type": "data_flow" + }, + { + "source": "sparse_build", + "target": "out_pkg", + "edge_type": "produces_artifact" + }, + { + "source": "util_sql", + "target": "target_resolve", + "edge_type": "uses_utility" + }, + { + "source": "util_pool", + "target": "state_precomp", + "edge_type": "uses_utility" + }, + { + "source": "util_takeup_s5", + "target": "takeup_rerand", + "edge_type": "uses_utility" + }, + { + "source": "util_scipy", + "target": "sparse_build", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 6, + "label": "Stage 6", + "title": "Stage 6: Weight Fitting (L0 Calibration)", + "description": "Fit log-weights using L0 HardConcrete gates on GPU", + "country": "us", + "nodes": [ + { + "id": "in_pkg_s6", + "label": "calibration_package.pkl", + "node_type": "input", + "description": "From Stage 5 \u2014 X_sparse, targets_df, initial_weights" + }, + { + "id": "modal_gpu", + "label": "Modal GPU Container", + "node_type": "external", + "description": "T4 / A10 / A100 / H100 \u2014 32GB RAM, 8 CPU" + }, + { + "id": "init_weights", + "label": "Compute Initial Weights", + "node_type": "process", + "description": "Population-proportional per CD" + }, + { + "id": "create_model", + "label": "Create SparseCalibrationWeights", + "node_type": "process", + "description": "n_features = 5.16M, init_keep_prob = 0.999" + }, + { + "id": "fit_model", + "label": "model.fit()", + "node_type": "process", + "description": "Adam optimizer \u2014 loss = RSE + \u03bb\u2080\u00b7L0(w) + \u03bb\u2082\u00b7\u2016w\u2016\u00b2" + }, + { + "id": "extract_weights", + "label": "Extract Weights", + "node_type": "process", + "description": "Deterministic gate threshold \u2014 produces exact zeros" + }, + { + "id": "out_weights", + "label": "calibration_weights.npy", + "node_type": "output", + "description": "Shape: (n_records \u00d7 n_clones) \u2014 most entries zero" + }, + { + "id": "out_geo_s6", + "label": "geography.npz", + "node_type": "output", + "description": "block_geoid, cd_geoid, county_fips, state_fips" + }, + { + "id": "out_diag", + "label": "unified_diagnostics.csv", + "node_type": "output", + "description": "Per-target error analysis" + }, + { + "id": "out_config_s6", + "label": "unified_run_config.json", + "node_type": "output", + "description": "Hyperparameters + SHA256 checksums" + }, + { + "id": "util_l0", + "label": "l0-python", + "node_type": "utility", + "description": "SparseCalibrationWeights \u2014 HardConcrete gates" + }, + { + "id": "util_pytorch", + "label": "PyTorch", + "node_type": "utility", + "description": "Adam optimizer, autograd, CUDA" + } + ], + "edges": [ + { + "source": "in_pkg_s6", + "target": "init_weights", + "edge_type": "data_flow" + }, + { + "source": "init_weights", + "target": "create_model", + "edge_type": "data_flow" + }, + { + "source": "create_model", + "target": "fit_model", + "edge_type": "data_flow" + }, + { + "source": "modal_gpu", + "target": "fit_model", + "edge_type": "runs_on_infra", + "label": "runs on" + }, + { + "source": "fit_model", + "target": "extract_weights", + "edge_type": "data_flow" + }, + { + "source": "extract_weights", + "target": "out_weights", + "edge_type": "produces_artifact" + }, + { + "source": "extract_weights", + "target": "out_geo_s6", + "edge_type": "produces_artifact" + }, + { + "source": "fit_model", + "target": "out_diag", + "edge_type": "produces_artifact" + }, + { + "source": "fit_model", + "target": "out_config_s6", + "edge_type": "produces_artifact" + }, + { + "source": "util_l0", + "target": "create_model", + "edge_type": "uses_utility" + }, + { + "source": "util_pytorch", + "target": "fit_model", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 7, + "label": "Stage 7", + "title": "Stage 7: Local Area H5 Build", + "description": "Build 51 state + 435 district + 1 city H5 files on Modal workers", + "country": "us", + "nodes": [ + { + "id": "in_weights_s7", + "label": "calibration_weights.npy", + "node_type": "input", + "description": "From Stage 6" + }, + { + "id": "in_dataset_s7", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "input", + "description": "From Stage 4" + }, + { + "id": "in_db_s7", + "label": "policy_data.db", + "node_type": "external", + "description": "CD list for partitioning" + }, + { + "id": "modal_coord", + "label": "Modal Coordinator", + "node_type": "external", + "description": "coordinate_publish() \u2014 8GB RAM, 24hr timeout" + }, + { + "id": "partition", + "label": "Partition Work", + "node_type": "process", + "description": "50 workers \u2014 weights: state by CD count, city=11, district=1" + }, + { + "id": "phase1", + "label": "Phase 1: States", + "node_type": "us_specific", + "description": "51 state H5 files (50 + DC) \u2014 workers in parallel" + }, + { + "id": "phase2", + "label": "Phase 2: Districts", + "node_type": "us_specific", + "description": "~435 congressional district H5 files \u2014 workers in parallel" + }, + { + "id": "phase3", + "label": "Phase 3: Cities", + "node_type": "us_specific", + "description": "NYC (5 counties, 13 CDs) \u2014 city probability filtering" + }, + { + "id": "build_h5", + "label": "build_h5() \u2014 Core Logic", + "node_type": "process", + "description": "18-step H5 construction \u2014 load, reshape, filter, clone, derive geo, SPM, takeup, write" + }, + { + "id": "worker_s7", + "label": "Modal Worker Container", + "node_type": "external", + "description": "16GB RAM, 1 CPU each, 8-hour timeout" + }, + { + "id": "geo_derive", + "label": "derive_geography_from_blocks()", + "node_type": "process", + "description": "15 geographic variables from block GEOID" + }, + { + "id": "spm_recalc", + "label": "SPM Threshold Recalculation", + "node_type": "process", + "description": "Local median rents, family composition, tenure type" + }, + { + "id": "takeup_apply", + "label": "Takeup Re-application", + "node_type": "process", + "description": "9 takeup variables \u2014 block-level seeded draws" + }, + { + "id": "out_states", + "label": "states/*.h5", + "node_type": "output", + "description": "51 files \u2014 AL.h5 through WY.h5" + }, + { + "id": "out_districts", + "label": "districts/*.h5", + "node_type": "output", + "description": "~435 files \u2014 NC-01.h5, CA-52.h5, ..." + }, + { + "id": "out_cities", + "label": "cities/*.h5", + "node_type": "output", + "description": "1 file \u2014 NYC.h5" + }, + { + "id": "out_manifest", + "label": "manifest.json", + "node_type": "output", + "description": "SHA256 checksums for all H5 files" + }, + { + "id": "util_build_h5", + "label": "publish_local_area.build_h5()", + "node_type": "utility", + "description": "calibration/publish_local_area.py" + }, + { + "id": "util_takeup_s7", + "label": "apply_block_takeup_to_arrays()", + "node_type": "utility", + "description": "utils/takeup.py" + } + ], + "edges": [ + { + "source": "in_weights_s7", + "target": "partition", + "edge_type": "data_flow" + }, + { + "source": "in_dataset_s7", + "target": "partition", + "edge_type": "data_flow" + }, + { + "source": "in_db_s7", + "target": "partition", + "edge_type": "external_source", + "label": "CD list" + }, + { + "source": "partition", + "target": "phase1", + "edge_type": "data_flow" + }, + { + "source": "phase1", + "target": "phase2", + "edge_type": "data_flow" + }, + { + "source": "phase2", + "target": "phase3", + "edge_type": "data_flow" + }, + { + "source": "phase1", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "phase2", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "phase3", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "build_h5", + "target": "geo_derive", + "edge_type": "data_flow" + }, + { + "source": "geo_derive", + "target": "spm_recalc", + "edge_type": "data_flow" + }, + { + "source": "spm_recalc", + "target": "takeup_apply", + "edge_type": "data_flow" + }, + { + "source": "modal_coord", + "target": "worker_s7", + "edge_type": "runs_on_infra", + "label": "orchestrates" + }, + { + "source": "worker_s7", + "target": "build_h5", + "edge_type": "runs_on_infra", + "label": "runs" + }, + { + "source": "phase1", + "target": "out_states", + "edge_type": "produces_artifact" + }, + { + "source": "phase2", + "target": "out_districts", + "edge_type": "produces_artifact" + }, + { + "source": "phase3", + "target": "out_cities", + "edge_type": "produces_artifact" + }, + { + "source": "build_h5", + "target": "out_manifest", + "edge_type": "produces_artifact" + }, + { + "source": "util_build_h5", + "target": "build_h5", + "edge_type": "uses_utility" + }, + { + "source": "util_takeup_s7", + "target": "takeup_apply", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 8, + "label": "Stage 8", + "title": "Stage 8: Validation & Promotion", + "description": "7-layer validation, staging upload, atomic promotion to production", + "country": "us", + "nodes": [ + { + "id": "in_h5s", + "label": "51 state + 435 district + 1 city H5s", + "node_type": "input", + "description": "From Stage 7 (Modal volume)" + }, + { + "id": "in_db_s8", + "label": "policy_data.db", + "node_type": "external", + "description": "Validation targets" + }, + { + "id": "v1", + "label": "Layer 1: Manifest Verification", + "node_type": "process", + "description": "SHA256 checksums per file \u2014 catches corruption" + }, + { + "id": "v2", + "label": "Layer 2: Structural Sanity", + "node_type": "process", + "description": "Weight non-negativity, entity ID uniqueness, no NaN/Inf, mapping integrity" + }, + { + "id": "v3", + "label": "Layer 3: Target-Based Validation", + "node_type": "process", + "description": "Full Microsimulation per area \u2014 compare sim values to targets" + }, + { + "id": "v4", + "label": "Layer 4: Smoke Test", + "node_type": "process", + "description": "Sum 20 key vars across 51 states \u2014 GDP ~$29T, pop ~335M" + }, + { + "id": "v5", + "label": "Layer 5: National H5 Validation", + "node_type": "process", + "description": ">30% deviation flagged \u2014 hardcoded reference values" + }, + { + "id": "v6", + "label": "Layer 6: Pre-Upload Validation", + "node_type": "process", + "description": "File size minimums (100MB/50MB), H5 structure, Microsim aggregates" + }, + { + "id": "v7", + "label": "Layer 7: Package Validation", + "node_type": "process", + "description": "Matrix achievability, target ratio analysis, zero-row detection" + }, + { + "id": "staging_upload", + "label": "Upload to Staging", + "node_type": "process", + "description": "upload_to_staging_hf() \u2014 batches of 50 files/commit" + }, + { + "id": "atomic_promote", + "label": "Atomic Promotion", + "node_type": "process", + "description": "promote_staging_to_production_hf() \u2014 single CommitOperationCopy commit" + }, + { + "id": "gcs_upload", + "label": "GCS Parallel Upload", + "node_type": "external", + "description": "gs://policyengine-us-data/ \u2014 version metadata on each blob" + }, + { + "id": "staging_cleanup", + "label": "Staging Cleanup", + "node_type": "process", + "description": "cleanup_staging_hf() \u2014 removes staging/ files" + }, + { + "id": "out_hf_prod", + "label": "HuggingFace Production", + "node_type": "external", + "description": "policyengine/policyengine-us-data \u2014 final published datasets" + }, + { + "id": "out_gcs", + "label": "Google Cloud Storage", + "node_type": "external", + "description": "gs://policyengine-us-data/ \u2014 CDN/backup" + }, + { + "id": "util_manifest_s8", + "label": "manifest.py", + "node_type": "utility", + "description": "generate_manifest(), verify_manifest()" + }, + { + "id": "util_sanity", + "label": "sanity_checks.py", + "node_type": "utility", + "description": "run_sanity_checks()" + }, + { + "id": "util_validate", + "label": "validate_staging.py", + "node_type": "utility", + "description": "Full target comparison" + }, + { + "id": "util_upload", + "label": "data_upload.py", + "node_type": "utility", + "description": "staging/promote/cleanup" + } + ], + "edges": [ + { + "source": "in_h5s", + "target": "v1", + "edge_type": "data_flow" + }, + { + "source": "in_db_s8", + "target": "v3", + "edge_type": "external_source", + "label": "targets" + }, + { + "source": "v1", + "target": "v2", + "edge_type": "data_flow" + }, + { + "source": "v2", + "target": "v3", + "edge_type": "data_flow" + }, + { + "source": "v3", + "target": "v4", + "edge_type": "data_flow" + }, + { + "source": "v4", + "target": "v5", + "edge_type": "data_flow" + }, + { + "source": "v5", + "target": "v6", + "edge_type": "data_flow" + }, + { + "source": "v6", + "target": "v7", + "edge_type": "data_flow" + }, + { + "source": "v7", + "target": "staging_upload", + "edge_type": "data_flow", + "label": "all pass" + }, + { + "source": "staging_upload", + "target": "atomic_promote", + "edge_type": "data_flow" + }, + { + "source": "atomic_promote", + "target": "gcs_upload", + "edge_type": "data_flow" + }, + { + "source": "gcs_upload", + "target": "staging_cleanup", + "edge_type": "data_flow" + }, + { + "source": "atomic_promote", + "target": "out_hf_prod", + "edge_type": "produces_artifact" + }, + { + "source": "gcs_upload", + "target": "out_gcs", + "edge_type": "produces_artifact" + }, + { + "source": "util_manifest_s8", + "target": "v1", + "edge_type": "uses_utility" + }, + { + "source": "util_sanity", + "target": "v2", + "edge_type": "uses_utility" + }, + { + "source": "util_validate", + "target": "v3", + "edge_type": "uses_utility" + }, + { + "source": "util_upload", + "target": "staging_upload", + "edge_type": "uses_utility" + } + ] + } + ], + "metadata": { + "generated_at": "2026-04-06T18:47:05.119469+00:00", + "total_nodes": 157, + "total_edges": 160 + } +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/eslint.config.mjs b/docs/pipeline-diagrams/demos/pipeline-demos/eslint.config.mjs new file mode 100644 index 000000000..05e726d1b --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/next.config.ts b/docs/pipeline-diagrams/demos/pipeline-demos/next.config.ts new file mode 100644 index 000000000..e9ffa3083 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/package-lock.json b/docs/pipeline-diagrams/demos/pipeline-demos/package-lock.json new file mode 100644 index 000000000..1ac321f60 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/package-lock.json @@ -0,0 +1,6825 @@ +{ + "name": "pipeline-demos", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pipeline-demos", + "version": "0.1.0", + "dependencies": { + "@dagrejs/dagre": "^3.0.0", + "@xyflow/react": "^12.10.2", + "elkjs": "^0.11.1", + "next": "16.2.2", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.2", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dagrejs/dagre": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-3.0.0.tgz", + "integrity": "sha512-ZzhnTy1rfuoew9Ez3EIw4L2znPGnYYhfn8vc9c4oB8iw6QAsszbiU0vRhlxWPFnmmNSFAkrYeF1PhM5m4lAN0Q==", + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "4.0.1" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-4.0.1.tgz", + "integrity": "sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA==", + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.2.tgz", + "integrity": "sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.2.tgz", + "integrity": "sha512-IOPbWzDQ+76AtjZioaCjpIY72xNSDMnarZ2GMQ4wjNLvnJEJHqxQwGFhgnIWLV9klb4g/+amg88Tk5OXVpyLTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.2.tgz", + "integrity": "sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.2.tgz", + "integrity": "sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.2.tgz", + "integrity": "sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.2.tgz", + "integrity": "sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.2.tgz", + "integrity": "sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.2.tgz", + "integrity": "sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.2.tgz", + "integrity": "sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.2.tgz", + "integrity": "sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@xyflow/react": { + "version": "12.10.2", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.10.2.tgz", + "integrity": "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==", + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.76", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.76", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.76.tgz", + "integrity": "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==", + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", + "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", + "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001786", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001786.tgz", + "integrity": "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz", + "integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==", + "license": "EPL-2.0" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", + "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.2.tgz", + "integrity": "sha512-6VlvEhwoug2JpVgjZDhyXrJXUEuPY++TddzIpTaIRvlvlXXFgvQUtm3+Zr84IjFm0lXtJt73w19JA08tOaZVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "16.2.2", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.2.tgz", + "integrity": "sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.2", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.2", + "@next/swc-darwin-x64": "16.2.2", + "@next/swc-linux-arm64-gnu": "16.2.2", + "@next/swc-linux-arm64-musl": "16.2.2", + "@next/swc-linux-x64-gnu": "16.2.2", + "@next/swc-linux-x64-musl": "16.2.2", + "@next/swc-win32-arm64-msvc": "16.2.2", + "@next/swc-win32-x64-msvc": "16.2.2", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/package.json b/docs/pipeline-diagrams/demos/pipeline-demos/package.json new file mode 100644 index 000000000..888db457a --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/package.json @@ -0,0 +1,29 @@ +{ + "name": "pipeline-demos", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@dagrejs/dagre": "^3.0.0", + "@xyflow/react": "^12.10.2", + "elkjs": "^0.11.1", + "next": "16.2.2", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.2", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/postcss.config.mjs b/docs/pipeline-diagrams/demos/pipeline-demos/postcss.config.mjs new file mode 100644 index 000000000..61e36849c --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/public/file.svg b/docs/pipeline-diagrams/demos/pipeline-demos/public/file.svg new file mode 100644 index 000000000..004145cdd --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/public/globe.svg b/docs/pipeline-diagrams/demos/pipeline-demos/public/globe.svg new file mode 100644 index 000000000..567f17b0d --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/public/next.svg b/docs/pipeline-diagrams/demos/pipeline-demos/public/next.svg new file mode 100644 index 000000000..5174b28c5 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/public/vercel.svg b/docs/pipeline-diagrams/demos/pipeline-demos/public/vercel.svg new file mode 100644 index 000000000..770539603 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/public/window.svg b/docs/pipeline-diagrams/demos/pipeline-demos/public/window.svg new file mode 100644 index 000000000..b2b2a44f6 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/demos/pipeline-demos/tsconfig.json b/docs/pipeline-diagrams/demos/pipeline-demos/tsconfig.json new file mode 100644 index 000000000..3a13f90a7 --- /dev/null +++ b/docs/pipeline-diagrams/demos/pipeline-demos/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/docs/pipeline-diagrams/manifest.json b/docs/pipeline-diagrams/manifest.json new file mode 100644 index 000000000..9ff0d6747 --- /dev/null +++ b/docs/pipeline-diagrams/manifest.json @@ -0,0 +1,202 @@ +{ + "countries": { + "us": { + "overviewTitle": "Pipeline Overview \u2014 Cross-Stage Data Flow", + "overviewDot": "dots/us/overview.dot", + "stages": [ + { + "id": 0, + "label": "Stage 0", + "desc": "Raw Data Download", + "title": "Stage 0: Raw Data Download", + "dotFile": "dots/us/stage_0.dot", + "sourceGlobs": [ + "policyengine_us_data/storage/download_private_prerequisites.py" + ] + }, + { + "id": 1, + "label": "Stage 1", + "desc": "Base Dataset Construction", + "title": "Stage 1: Base Dataset Construction", + "dotFile": "dots/us/stage_1.dot", + "sourceGlobs": [ + "policyengine_us_data/datasets/cps/cps.py", + "policyengine_us_data/datasets/puf/puf.py", + "policyengine_us_data/datasets/puf/irs_puf.py", + "policyengine_us_data/datasets/acs/acs.py", + "policyengine_us_data/datasets/scf/scf.py" + ] + }, + { + "id": 2, + "label": "Stage 2", + "desc": "Extended CPS (PUF Clone)", + "title": "Stage 2: Extended CPS (PUF Clone)", + "dotFile": "dots/us/stage_2.dot", + "sourceGlobs": [ + "policyengine_us_data/datasets/cps/extended_cps.py", + "policyengine_us_data/calibration/puf_impute.py" + ] + }, + { + "id": 3, + "label": "Stage 3", + "desc": "Stratified CPS", + "title": "Stage 3: Stratified CPS + Enhanced CPS Reweighting", + "dotFile": "dots/us/stage_3.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/create_stratified_cps.py" + ] + }, + { + "id": 4, + "label": "Stage 4", + "desc": "Source Imputation", + "title": "Stage 4: Source Imputation (ACS + SIPP + SCF)", + "dotFile": "dots/us/stage_4.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/create_source_imputed_cps.py", + "policyengine_us_data/calibration/source_impute.py", + "policyengine_us_data/calibration/clone_and_assign.py" + ] + }, + { + "id": 5, + "label": "Stage 5", + "desc": "Matrix Build", + "title": "Stage 5: Matrix Build (Calibration Target Construction)", + "dotFile": "dots/us/stage_5.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/unified_matrix_builder.py", + "policyengine_us_data/calibration/unified_calibration.py", + "policyengine_us_data/calibration/target_config.yaml", + "policyengine_us_data/db/*.py" + ] + }, + { + "id": 6, + "label": "Stage 6", + "desc": "Weight Fitting (L0)", + "title": "Stage 6: Weight Fitting (L0 Calibration)", + "dotFile": "dots/us/stage_6.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/unified_calibration.py" + ] + }, + { + "id": 7, + "label": "Stage 7", + "desc": "Local Area H5 Build", + "title": "Stage 7: Local Area H5 Build", + "dotFile": "dots/us/stage_7.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/publish_local_area.py", + "policyengine_us_data/calibration/block_assignment.py", + "policyengine_us_data/calibration/calibration_utils.py", + "modal_app/local_area.py" + ] + }, + { + "id": 8, + "label": "Stage 8", + "desc": "Validation & Promotion", + "title": "Stage 8: Validation & Promotion", + "dotFile": "dots/us/stage_8.dot", + "sourceGlobs": [ + "policyengine_us_data/calibration/validate_staging.py", + "policyengine_us_data/calibration/sanity_checks.py", + "policyengine_us_data/calibration/check_staging_sums.py", + "policyengine_us_data/storage/upload_completed_datasets.py" + ] + } + ] + }, + "uk": { + "overviewTitle": "UK pipeline overview", + "overviewDot": "dots/uk/overview.dot", + "stages": [ + { + "id": 0, + "label": "Stage 0", + "desc": "Raw Data Download", + "title": "Stage 0: Raw data download", + "dotFile": "dots/uk/stage_0.dot", + "sourceGlobs": [] + }, + { + "id": 1, + "label": "Stage 1", + "desc": "Base Dataset Construction", + "title": "Stage 1: Base dataset construction", + "dotFile": "dots/uk/stage_1.dot", + "sourceGlobs": [] + }, + { + "id": 2, + "label": "Stage 2", + "desc": "Income Enhancement (SPI)", + "title": "Stage 2: Income enhancement (SPI synthetic)", + "dotFile": "dots/uk/stage_2.dot", + "sourceGlobs": [] + }, + { + "id": 3, + "label": "Stage 3", + "desc": "Stratification", + "title": "Stage 3: Stratification (absent in UK)", + "dotFile": "dots/uk/stage_3.dot", + "absent": true, + "sourceGlobs": [] + }, + { + "id": 4, + "label": "Stage 4", + "desc": "Source Imputation", + "title": "Stage 4: Source imputation (WAS + LCFS + ETB + CG)", + "dotFile": "dots/uk/stage_4.dot", + "sourceGlobs": [] + }, + { + "id": 5, + "label": "Stage 5", + "desc": "Matrix Build", + "title": "Stage 5: Matrix build (calibration target construction)", + "dotFile": "dots/uk/stage_5.dot", + "sourceGlobs": [] + }, + { + "id": 6, + "label": "Stage 6", + "desc": "Weight Fitting (Torch)", + "title": "Stage 6: Weight fitting (Torch optimizer)", + "dotFile": "dots/uk/stage_6.dot", + "sourceGlobs": [] + }, + { + "id": 7, + "label": "Stage 7", + "desc": "Local Area Calibration", + "title": "Stage 7: Local area calibration", + "dotFile": "dots/uk/stage_7.dot", + "sourceGlobs": [] + }, + { + "id": 8, + "label": "Stage 8", + "desc": "Validation & Output", + "title": "Stage 8: Validation & output", + "dotFile": "dots/uk/stage_8.dot", + "sourceGlobs": [] + } + ] + } + }, + "crossStageUtilities": { + "policyengine_us_data/utils/takeup.py": [1, 2, 4, 5, 7], + "policyengine_us_data/utils/loss.py": [3, 5], + "policyengine_us_data/utils/spm.py": [1, 7], + "policyengine_us_data/utils/uprating.py": [1], + "policyengine_us_data/utils/l0.py": [6] + } +} diff --git a/docs/pipeline-diagrams/next.config.mjs b/docs/pipeline-diagrams/next.config.mjs new file mode 100644 index 000000000..f26ac370c --- /dev/null +++ b/docs/pipeline-diagrams/next.config.mjs @@ -0,0 +1,3 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; +export default nextConfig; diff --git a/docs/pipeline-diagrams/package-lock.json b/docs/pipeline-diagrams/package-lock.json new file mode 100644 index 000000000..941e9bb2b --- /dev/null +++ b/docs/pipeline-diagrams/package-lock.json @@ -0,0 +1,1600 @@ +{ + "name": "pipeline-diagrams", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pipeline-diagrams", + "version": "0.0.0", + "dependencies": { + "@hpcc-js/wasm-graphviz": "^1.6.1", + "@tailwindcss/postcss": "^4.0.0", + "next": "^15.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwindcss": "^4.0.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@hpcc-js/wasm-graphviz": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm-graphviz/-/wasm-graphviz-1.21.2.tgz", + "integrity": "sha512-UKvY3MSI1CfkOsVL6Wgg9m8S0x/28u27NgFX5UG+myTYfxU0Sl6jYKXQkcEQLZfulHKQjgHjz5/Rpw5R0CC3/g==", + "license": "Apache-2.0" + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@next/env": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.14.tgz", + "integrity": "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.14.tgz", + "integrity": "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.14.tgz", + "integrity": "sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.14.tgz", + "integrity": "sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.14.tgz", + "integrity": "sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.14.tgz", + "integrity": "sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.14.tgz", + "integrity": "sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.14.tgz", + "integrity": "sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.14.tgz", + "integrity": "sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001780", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz", + "integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.5.14", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", + "integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.14", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.14", + "@next/swc-darwin-x64": "15.5.14", + "@next/swc-linux-arm64-gnu": "15.5.14", + "@next/swc-linux-arm64-musl": "15.5.14", + "@next/swc-linux-x64-gnu": "15.5.14", + "@next/swc-linux-x64-musl": "15.5.14", + "@next/swc-win32-arm64-msvc": "15.5.14", + "@next/swc-win32-x64-msvc": "15.5.14", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + } + } +} diff --git a/docs/pipeline-diagrams/package.json b/docs/pipeline-diagrams/package.json new file mode 100644 index 000000000..22e321e18 --- /dev/null +++ b/docs/pipeline-diagrams/package.json @@ -0,0 +1,20 @@ +{ + "name": "pipeline-diagrams", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "update-diagrams": "node scripts/update-diagrams.mjs" + }, + "dependencies": { + "@hpcc-js/wasm-graphviz": "^1.6.1", + "@tailwindcss/postcss": "^4.0.0", + "next": "^15.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwindcss": "^4.0.0" + } +} diff --git a/docs/pipeline-diagrams/pipeline.json b/docs/pipeline-diagrams/pipeline.json new file mode 100644 index 000000000..df31f401a --- /dev/null +++ b/docs/pipeline-diagrams/pipeline.json @@ -0,0 +1,1922 @@ +{ + "stages": [ + { + "id": 0, + "label": "Stage 0", + "title": "Stage 0: Raw Data Download", + "description": "Download raw survey data from Census, IRS, Federal Reserve, and HuggingFace", + "country": "us", + "nodes": [ + { + "id": "cps_url", + "label": "Census CPS ASEC", + "node_type": "input", + "description": "ZIP with CSV files from www2.census.gov" + }, + { + "id": "acs_url", + "label": "Census ACS PUMS", + "node_type": "input", + "description": "Person + Household CSVs, 1-Year estimates" + }, + { + "id": "scf_url", + "label": "Federal Reserve SCF", + "node_type": "input", + "description": "Stata format (.dta), Survey of Consumer Finances" + }, + { + "id": "hf_private", + "label": "HuggingFace Private Repo", + "node_type": "external", + "description": "policyengine/irs-soi-puf \u2014 requires HUGGING_FACE_TOKEN" + }, + { + "id": "hf_public", + "label": "HuggingFace Public Repo", + "node_type": "external", + "description": "policyengine/policyengine-us-data \u2014 SIPP, block distributions, policy_data.db" + }, + { + "id": "download_http", + "label": "HTTP Download + ZIP Extract", + "node_type": "process", + "description": "requests.get() with streaming, tqdm progress bar" + }, + { + "id": "download_hf", + "label": "HuggingFace Hub Download", + "node_type": "process", + "description": "hf_hub_download(), token-authenticated for private repos" + }, + { + "id": "csv_parse", + "label": "CSV/Stata Parsing", + "node_type": "process", + "description": "pandas read_csv/read_stata, entity table construction" + }, + { + "id": "out_cps_raw", + "label": "census_cps_2024.h5", + "node_type": "output", + "description": "5 entity tables: Person, Family, Household, Tax Unit, SPM Unit" + }, + { + "id": "out_acs_raw", + "label": "census_acs_2022.h5", + "node_type": "output", + "description": "Person + Household tables" + }, + { + "id": "out_puf_raw", + "label": "irs_puf_2015.h5", + "node_type": "output", + "description": "PUF + Demographics tables" + }, + { + "id": "out_soi", + "label": "soi.csv", + "node_type": "output", + "description": "SOI aggregate statistics" + }, + { + "id": "out_scf", + "label": "SCF raw data", + "node_type": "output", + "description": "Stata \u2192 DataFrame, auto loans + net worth" + }, + { + "id": "out_sipp", + "label": "pu2023_slim.csv", + "node_type": "output", + "description": "SIPP microdata (pipe-delimited)" + }, + { + "id": "out_block", + "label": "block_cd_distributions.csv.gz", + "node_type": "output", + "description": "Census block populations, block-to-CD crosswalk" + }, + { + "id": "out_pop", + "label": "np2023_d5_mid.csv", + "node_type": "output", + "description": "Census population projections" + }, + { + "id": "out_calibration_db", + "label": "policy_data.db", + "node_type": "output", + "description": "SQLite calibration targets database" + }, + { + "id": "util_storage", + "label": "STORAGE_FOLDER", + "node_type": "utility", + "description": "policyengine_us_data/storage/ \u2014 all downloads cached here" + } + ], + "edges": [ + { + "source": "cps_url", + "target": "download_http", + "edge_type": "external_source", + "label": "CPS ASEC ZIP" + }, + { + "source": "acs_url", + "target": "download_http", + "edge_type": "external_source", + "label": "ACS PUMS CSV" + }, + { + "source": "scf_url", + "target": "download_http", + "edge_type": "external_source", + "label": "SCF .dta" + }, + { + "source": "hf_private", + "target": "download_hf", + "edge_type": "external_source", + "label": "PUF, demographics, SOI, pop" + }, + { + "source": "hf_public", + "target": "download_hf", + "edge_type": "external_source", + "label": "SIPP, block, policy_data.db" + }, + { + "source": "download_http", + "target": "csv_parse", + "edge_type": "data_flow", + "label": "raw files" + }, + { + "source": "download_hf", + "target": "csv_parse", + "edge_type": "data_flow", + "label": "raw files" + }, + { + "source": "csv_parse", + "target": "out_cps_raw", + "edge_type": "produces_artifact", + "label": "census_cps_2024.h5" + }, + { + "source": "csv_parse", + "target": "out_acs_raw", + "edge_type": "produces_artifact", + "label": "census_acs_2022.h5" + }, + { + "source": "csv_parse", + "target": "out_puf_raw", + "edge_type": "produces_artifact", + "label": "irs_puf_2015.h5" + }, + { + "source": "csv_parse", + "target": "out_soi", + "edge_type": "produces_artifact", + "label": "soi.csv" + }, + { + "source": "download_http", + "target": "out_scf", + "edge_type": "produces_artifact", + "label": "SCF raw data" + }, + { + "source": "download_hf", + "target": "out_sipp", + "edge_type": "produces_artifact", + "label": "pu2023_slim.csv" + }, + { + "source": "download_hf", + "target": "out_block", + "edge_type": "produces_artifact", + "label": "block_cd_distributions.csv.gz" + }, + { + "source": "download_hf", + "target": "out_pop", + "edge_type": "produces_artifact", + "label": "np2023_d5_mid.csv" + }, + { + "source": "download_hf", + "target": "out_calibration_db", + "edge_type": "produces_artifact", + "label": "policy_data.db" + } + ] + }, + { + "id": 1, + "label": "Stage 1", + "title": "Stage 1: Base Dataset Construction", + "description": "Build CPS 2024 and PUF 2024 from raw survey data", + "country": "us", + "nodes": [ + { + "id": "in_census_cps", + "label": "census_cps_2024.h5", + "node_type": "input", + "description": "Raw CPS ASEC from Stage 0" + }, + { + "id": "in_census_cps_prev", + "label": "census_cps_2023.h5", + "node_type": "input", + "description": "Previous year CPS for income matching" + }, + { + "id": "in_acs", + "label": "ACS 2022", + "node_type": "input", + "description": "Training data for rent QRF" + }, + { + "id": "in_sipp", + "label": "SIPP 2023", + "node_type": "input", + "description": "Training data for tips QRF" + }, + { + "id": "in_scf", + "label": "SCF 2022", + "node_type": "input", + "description": "Training data for auto loans QRF" + }, + { + "id": "in_uprating", + "label": "uprating_factors.csv", + "node_type": "input", + "description": "PE uprating factors table" + }, + { + "id": "out_cps", + "label": "cps_2024.h5", + "node_type": "output", + "description": "Dataset.ARRAYS format, ~65K households (half-sample)" + }, + { + "id": "out_puf", + "label": "puf_2024.h5", + "node_type": "output", + "description": "Dataset.ARRAYS format" + }, + { + "id": "in_irs_puf", + "label": "irs_puf_2015.h5", + "node_type": "input", + "description": "Raw IRS PUF from Stage 0" + }, + { + "id": "in_demographics", + "label": "demographics_2015.csv", + "node_type": "input", + "description": "PUF demographics from HuggingFace" + }, + { + "id": "util_seeded_rng", + "label": "seeded_rng()", + "node_type": "utility", + "description": "Deterministic per-variable RNG" + }, + { + "id": "util_qrf", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + }, + { + "id": "util_retirement_limits", + "label": "get_retirement_limits()", + "node_type": "utility", + "description": "IRS contribution limits" + }, + { + "id": "add_rent", + "label": "Rent Imputation (QRF)", + "node_type": "process", + "description": "Impute rent and real estate taxes using QRF from ACS 2022", + "details": "10K sampled household heads as training data", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "impute_puf_demographics", + "label": "Impute PUF Demographics", + "node_type": "process", + "description": "QRF imputation for age, gender, and earnings split", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_ssn_card_type", + "label": "SSN Card Type", + "node_type": "us_specific", + "description": "US immigration classification from 14 ASEC conditions", + "details": "Undocumented target: 13M; SSN card types 0-3", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_personal_variables", + "label": "Add Personal Variables", + "node_type": "process", + "description": "Age, sex, disability, occupation, overtime flags", + "details": "80+ ages randomized to 80-84; 12 overtime occupation flags", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_id_variables", + "label": "Add ID Variables", + "node_type": "process", + "description": "Create person_id, household_id, tax_unit_id, spm_unit_id, marital_unit_id", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_tips", + "label": "Tips Imputation (QRF)", + "node_type": "process", + "description": "Impute tip income and liquid assets from SIPP 2023", + "details": "Models cached as pickle; QRF fit_predict()", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_auto_loan", + "label": "Auto Loan / Net Worth (QRF)", + "node_type": "process", + "description": "Impute auto loan balance, interest, and net worth from SCF 2022", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "simulate_qbi", + "label": "QBI Simulation", + "node_type": "process", + "description": "Simulate W-2 wages, UBIA, and SSTB for Section 199A", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_spm_variables", + "label": "SPM Variables", + "node_type": "process", + "description": "SPM thresholds and transfers (SNAP, housing, energy subsidies)", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "preprocess_puf", + "label": "Preprocess PUF", + "node_type": "process", + "description": "Rename 60+ IRS variables to PolicyEngine names", + "details": "E00200 \u2192 employment_income, etc.", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "add_personal_income_variables", + "label": "Add Income Variables", + "node_type": "process", + "description": "30+ income types with splits", + "details": "SS classified by reason codes; retirement split by account type; capital gains 88% LT / 12% ST", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_previous_year_income", + "label": "Previous Year Income", + "node_type": "process", + "description": "Cross-year PERIDNUM linking for prior-year income", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_household_variables", + "label": "Household Variables", + "node_type": "process", + "description": "State FIPS, county FIPS, NYC flag from county codes", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_takeup", + "label": "Benefit Takeup", + "node_type": "us_specific", + "description": "Stochastic takeup for 9 benefit programs", + "details": "SNAP, ACA, Medicaid, EITC, SSI, TANF, WIC, Head Start; state-specific Medicaid rates", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "downsample", + "label": "Downsampling", + "node_type": "process", + "description": "frac=0.5 for CPS_2024 using Microsimulation.subsample()", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + } + ], + "edges": [ + { + "source": "in_census_cps", + "target": "add_id_variables", + "edge_type": "data_flow", + "label": "raw CPS tables" + }, + { + "source": "add_id_variables", + "target": "add_personal_variables", + "edge_type": "data_flow" + }, + { + "source": "add_personal_variables", + "target": "add_personal_income_variables", + "edge_type": "data_flow" + }, + { + "source": "add_personal_income_variables", + "target": "add_previous_year_income", + "edge_type": "data_flow" + }, + { + "source": "in_census_cps_prev", + "target": "add_previous_year_income", + "edge_type": "data_flow", + "label": "prior year PERIDNUM" + }, + { + "source": "add_previous_year_income", + "target": "add_ssn_card_type", + "edge_type": "data_flow" + }, + { + "source": "add_ssn_card_type", + "target": "add_spm_variables", + "edge_type": "data_flow" + }, + { + "source": "add_spm_variables", + "target": "add_household_variables", + "edge_type": "data_flow" + }, + { + "source": "add_household_variables", + "target": "add_rent", + "edge_type": "data_flow" + }, + { + "source": "in_acs", + "target": "add_rent", + "edge_type": "external_source", + "label": "ACS training data" + }, + { + "source": "add_rent", + "target": "add_tips", + "edge_type": "data_flow" + }, + { + "source": "in_sipp", + "target": "add_tips", + "edge_type": "external_source", + "label": "SIPP training data" + }, + { + "source": "add_tips", + "target": "add_auto_loan", + "edge_type": "data_flow" + }, + { + "source": "in_scf", + "target": "add_auto_loan", + "edge_type": "external_source", + "label": "SCF training data" + }, + { + "source": "add_auto_loan", + "target": "add_takeup", + "edge_type": "data_flow" + }, + { + "source": "add_takeup", + "target": "downsample", + "edge_type": "data_flow" + }, + { + "source": "downsample", + "target": "out_cps", + "edge_type": "produces_artifact", + "label": "cps_2024.h5" + }, + { + "source": "in_irs_puf", + "target": "preprocess_puf", + "edge_type": "data_flow", + "label": "raw PUF records" + }, + { + "source": "preprocess_puf", + "target": "simulate_qbi", + "edge_type": "data_flow" + }, + { + "source": "simulate_qbi", + "target": "impute_puf_demographics", + "edge_type": "data_flow" + }, + { + "source": "in_demographics", + "target": "impute_puf_demographics", + "edge_type": "data_flow", + "label": "demographics_2015.csv" + }, + { + "source": "impute_puf_demographics", + "target": "out_puf", + "edge_type": "produces_artifact", + "label": "puf_2024.h5" + }, + { + "source": "in_uprating", + "target": "out_puf", + "edge_type": "data_flow", + "label": "SOI growth rates" + }, + { + "source": "util_seeded_rng", + "target": "add_takeup", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_rent", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_tips", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "add_auto_loan", + "edge_type": "uses_utility" + }, + { + "source": "util_retirement_limits", + "target": "add_personal_income_variables", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf", + "target": "impute_puf_demographics", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 2, + "label": "Stage 2", + "title": "Stage 2: Extended CPS (PUF Clone)", + "description": "Merge CPS + PUF via cloning, impute 64 income vars + 51 override vars via QRF", + "country": "us", + "nodes": [ + { + "id": "in_cps_s2", + "label": "CPS_2024_Full", + "node_type": "input", + "description": "From Stage 1 (full sample)" + }, + { + "id": "in_puf_s2", + "label": "PUF_2024", + "node_type": "input", + "description": "From Stage 1" + }, + { + "id": "in_blocks_s2", + "label": "block_cd_distributions.csv.gz", + "node_type": "input", + "description": "Census block populations" + }, + { + "id": "geo_assign_s2", + "label": "Geography Assignment", + "node_type": "process", + "description": "assign_random_geography() \u2014 population-weighted block draw" + }, + { + "id": "record_double", + "label": "Record Doubling", + "node_type": "process", + "description": "puf_clone_dataset() \u2014 CPS half keeps originals, PUF half starts with zero weight" + }, + { + "id": "qrf_pass1", + "label": "QRF Pass 1: Full Imputation", + "node_type": "process", + "description": "64 income variables \u2014 training on PUF ~20K records, 7 demographic predictors" + }, + { + "id": "qrf_pass2", + "label": "QRF Pass 2: Override Imputation", + "node_type": "process", + "description": "51 variables (both halves) \u2014 partnership, S-corp, charitable, mortgage, credits" + }, + { + "id": "retire_impute", + "label": "Retirement Contribution Imputation", + "node_type": "process", + "description": "401k, IRA, SE pension \u2014 IRS limits + catch-up applied" + }, + { + "id": "weeks_impute", + "label": "Weeks Unemployed Imputation", + "node_type": "process", + "description": "QRF on CPS weeks_unemployed \u2014 clips [0, 52], zero if no UC" + }, + { + "id": "ss_reconcile", + "label": "SS Sub-component Reconciliation", + "node_type": "process", + "description": "Retirement/Disability/Survivors/Dependents \u2014 scaled to match PUF total" + }, + { + "id": "cps_only", + "label": "CPS-Only Variable Re-imputation", + "node_type": "process", + "description": "80 variables for PUF half \u2014 transfers, SPM, medical, hours, retirement" + }, + { + "id": "formula_drop", + "label": "Formula Variable Dropping", + "node_type": "process", + "description": "Remove PE-computed variables, rename employment_income \u2192 employment_income_before_lsr" + }, + { + "id": "out_ext", + "label": "extended_cps_2024.h5", + "node_type": "output", + "description": "~260K households (doubled), CPS half + PUF half" + }, + { + "id": "util_puf_clone", + "label": "puf_clone_dataset()", + "node_type": "utility", + "description": "calibration/puf_impute.py" + }, + { + "id": "util_qrf_s2", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + } + ], + "edges": [ + { + "source": "in_cps_s2", + "target": "geo_assign_s2", + "edge_type": "data_flow", + "label": "CPS records" + }, + { + "source": "in_blocks_s2", + "target": "geo_assign_s2", + "edge_type": "data_flow", + "label": "block populations" + }, + { + "source": "in_puf_s2", + "target": "record_double", + "edge_type": "data_flow", + "label": "PUF records" + }, + { + "source": "in_cps_s2", + "target": "record_double", + "edge_type": "data_flow", + "label": "CPS records" + }, + { + "source": "geo_assign_s2", + "target": "record_double", + "edge_type": "data_flow" + }, + { + "source": "record_double", + "target": "qrf_pass1", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass1", + "target": "qrf_pass2", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass2", + "target": "retire_impute", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass2", + "target": "weeks_impute", + "edge_type": "data_flow" + }, + { + "source": "retire_impute", + "target": "ss_reconcile", + "edge_type": "data_flow" + }, + { + "source": "weeks_impute", + "target": "ss_reconcile", + "edge_type": "data_flow" + }, + { + "source": "ss_reconcile", + "target": "cps_only", + "edge_type": "data_flow" + }, + { + "source": "cps_only", + "target": "formula_drop", + "edge_type": "data_flow" + }, + { + "source": "formula_drop", + "target": "out_ext", + "edge_type": "produces_artifact" + }, + { + "source": "util_puf_clone", + "target": "record_double", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "qrf_pass1", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "qrf_pass2", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "cps_only", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 3, + "label": "Stage 3", + "title": "Stage 3: Stratified CPS + Enhanced CPS Reweighting", + "description": "Stratify by income (keep top 1%), reweight to match calibration targets", + "country": "us", + "nodes": [ + { + "id": "in_ext_cps", + "label": "extended_cps_2024.h5", + "node_type": "input", + "description": "From Stage 2 (~260K HH)" + }, + { + "id": "calc_agi", + "label": "Calculate AGI", + "node_type": "process", + "description": "Microsimulation.calculate() \u2014 adjusted_gross_income mapped to household" + }, + { + "id": "strat_top", + "label": "Retain Top 1% by AGI", + "node_type": "process", + "description": "All high-income households kept \u2014 preserves tail representation" + }, + { + "id": "strat_sample", + "label": "Uniform Sample Remaining 99%", + "node_type": "process", + "description": "Target: ~12,000 households \u2014 optional 1.5x oversample of bottom 25%" + }, + { + "id": "out_strat", + "label": "stratified_extended_cps_2024.h5", + "node_type": "output", + "description": "~12K households \u2014 input to Stages 4-6" + }, + { + "id": "in_ext_half", + "label": "ExtendedCPS_2024_Half", + "node_type": "input", + "description": "Half-sample from Stage 2" + }, + { + "id": "build_loss", + "label": "build_loss_matrix()", + "node_type": "process", + "description": "Hundreds of calibration targets \u2014 returns (matrix, target_vector)" + }, + { + "id": "t_soi", + "label": "IRS SOI", + "node_type": "external", + "description": "AGI, income types, filer counts by AGI band / filing status" + }, + { + "id": "t_census", + "label": "Census Population", + "node_type": "external", + "description": "86 single-year age groups from np2023_d5_mid.csv" + }, + { + "id": "t_cbo", + "label": "CBO Budget", + "node_type": "external", + "description": "Income tax, SNAP, SS, SSI, UC" + }, + { + "id": "t_state", + "label": "State Targets", + "node_type": "us_specific", + "description": "50 states + DC \u2014 population, AGI, ACA, SNAP, age groups" + }, + { + "id": "t_jct", + "label": "JCT Tax Expenditures", + "node_type": "external", + "description": "SALT, medical, charitable \u2014 counterfactual simulations" + }, + { + "id": "reweight", + "label": "reweight()", + "node_type": "process", + "description": "PyTorch Adam + L0 HardConcrete \u2014 500 epochs, lr=0.2" + }, + { + "id": "weight_validate", + "label": "Weight Validation", + "node_type": "process", + "description": "No NaN, no negatives \u2014 100M < total HH < 200M" + }, + { + "id": "out_enhanced", + "label": "enhanced_cps_2024.h5", + "node_type": "output", + "description": "Reweighted production simulation dataset" + }, + { + "id": "util_loss", + "label": "build_loss_matrix()", + "node_type": "utility", + "description": "utils/loss.py" + }, + { + "id": "util_l0_s3", + "label": "HardConcrete L0", + "node_type": "utility", + "description": "utils/l0.py" + } + ], + "edges": [ + { + "source": "in_ext_cps", + "target": "calc_agi", + "edge_type": "data_flow" + }, + { + "source": "calc_agi", + "target": "strat_top", + "edge_type": "data_flow" + }, + { + "source": "strat_top", + "target": "strat_sample", + "edge_type": "data_flow" + }, + { + "source": "strat_top", + "target": "out_strat", + "edge_type": "data_flow", + "label": "top 1%" + }, + { + "source": "strat_sample", + "target": "out_strat", + "edge_type": "data_flow", + "label": "sampled 99%" + }, + { + "source": "in_ext_half", + "target": "build_loss", + "edge_type": "data_flow" + }, + { + "source": "t_soi", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_census", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_cbo", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_jct", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "t_state", + "target": "build_loss", + "edge_type": "external_source" + }, + { + "source": "build_loss", + "target": "reweight", + "edge_type": "data_flow", + "label": "(matrix, targets)" + }, + { + "source": "reweight", + "target": "weight_validate", + "edge_type": "data_flow" + }, + { + "source": "weight_validate", + "target": "out_enhanced", + "edge_type": "produces_artifact" + }, + { + "source": "util_loss", + "target": "build_loss", + "edge_type": "uses_utility" + }, + { + "source": "util_l0_s3", + "target": "reweight", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 4, + "label": "Stage 4", + "title": "Stage 4: Source Imputation (ACS + SIPP + SCF)", + "description": "Impute wealth/assets from external surveys onto stratified CPS via QRF", + "country": "us", + "nodes": [ + { + "id": "in_strat_s4", + "label": "stratified_extended_cps_2024.h5", + "node_type": "input", + "description": "From Stage 3 (~12K HH)" + }, + { + "id": "in_acs_s4", + "label": "ACS_2022", + "node_type": "input", + "description": "American Community Survey \u2014 has state_fips predictor" + }, + { + "id": "in_sipp_s4", + "label": "SIPP 2023", + "node_type": "external", + "description": "pu2023_slim.csv from HuggingFace" + }, + { + "id": "in_scf_s4", + "label": "SCF_2022", + "node_type": "input", + "description": "Survey of Consumer Finances \u2014 50% random subsample" + }, + { + "id": "geo_assign_s4", + "label": "Geography Assignment", + "node_type": "process", + "description": "assign_random_geography(n_clones=1) \u2014 block draw + state FIPS" + }, + { + "id": "acs_qrf", + "label": "ACS QRF Imputation", + "node_type": "process", + "description": "10 predictors incl. state_fips \u2014 imputes rent, real_estate_taxes" + }, + { + "id": "sipp_tips_qrf", + "label": "SIPP Tips QRF", + "node_type": "process", + "description": "4 predictors, NO state \u2014 imputes tip_income" + }, + { + "id": "sipp_assets_qrf", + "label": "SIPP Assets QRF", + "node_type": "process", + "description": "5 predictors \u2014 imputes bank_account_assets, stock_assets, bond_assets" + }, + { + "id": "scf_qrf", + "label": "SCF QRF Imputation", + "node_type": "process", + "description": "8 predictors \u2014 imputes net_worth, auto_loan_balance, auto_loan_interest" + }, + { + "id": "out_imputed", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "output", + "description": "Enriched with ACS/SIPP/SCF vars \u2014 uploaded to HuggingFace" + }, + { + "id": "util_clone_assign", + "label": "clone_and_assign.py", + "node_type": "utility", + "description": "Geography assignment" + }, + { + "id": "util_qrf_s4", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + } + ], + "edges": [ + { + "source": "in_strat_s4", + "target": "geo_assign_s4", + "edge_type": "data_flow" + }, + { + "source": "geo_assign_s4", + "target": "acs_qrf", + "edge_type": "data_flow", + "label": "state_fips" + }, + { + "source": "in_acs_s4", + "target": "acs_qrf", + "edge_type": "data_flow" + }, + { + "source": "in_sipp_s4", + "target": "sipp_tips_qrf", + "edge_type": "external_source" + }, + { + "source": "in_sipp_s4", + "target": "sipp_assets_qrf", + "edge_type": "external_source" + }, + { + "source": "in_scf_s4", + "target": "scf_qrf", + "edge_type": "external_source" + }, + { + "source": "acs_qrf", + "target": "sipp_tips_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "sipp_tips_qrf", + "target": "sipp_assets_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "sipp_assets_qrf", + "target": "scf_qrf", + "edge_type": "data_flow", + "label": "chain" + }, + { + "source": "scf_qrf", + "target": "out_imputed", + "edge_type": "produces_artifact" + }, + { + "source": "util_clone_assign", + "target": "geo_assign_s4", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "acs_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "sipp_tips_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "sipp_assets_qrf", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s4", + "target": "scf_qrf", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 5, + "label": "Stage 5", + "title": "Stage 5: Matrix Build (Calibration Target Construction)", + "description": "Build sparse calibration matrix (targets \u00d7 households \u00d7 clones)", + "country": "us", + "nodes": [ + { + "id": "in_cps_s5", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "input", + "description": "From Stage 4 (~12K HH)" + }, + { + "id": "in_db_s5", + "label": "policy_data.db", + "node_type": "external", + "description": "SQLite calibration database \u2014 10-step ETL" + }, + { + "id": "in_config_s5", + "label": "target_config.yaml", + "node_type": "input", + "description": "Active target include list" + }, + { + "id": "in_blocks_s5", + "label": "block_cd_distributions.csv.gz", + "node_type": "input", + "description": "Census block populations" + }, + { + "id": "target_resolve", + "label": "Target Resolution", + "node_type": "process", + "description": "SQL query to target_overview \u2014 ~8,000 rows total" + }, + { + "id": "target_uprate", + "label": "Target Uprating", + "node_type": "process", + "description": "CPI-U for dollars, pop growth for counts" + }, + { + "id": "geo_build", + "label": "Geography Index Build", + "node_type": "process", + "description": "state_to_cols, cd_to_cols maps" + }, + { + "id": "constraint_resolve", + "label": "Constraint Resolution", + "node_type": "process", + "description": "Non-geographic constraints from DB (age, medicaid, filer)" + }, + { + "id": "state_precomp", + "label": "Per-State Precomputation", + "node_type": "us_specific", + "description": "51 fresh Microsimulations \u2014 most expensive step" + }, + { + "id": "clone_assembly", + "label": "Clone Assembly", + "node_type": "process", + "description": "430 clones \u00d7 12K records = 5.16M columns" + }, + { + "id": "takeup_rerand", + "label": "Block-Level Takeup Re-randomization", + "node_type": "process", + "description": "Seeded on (block_geoid, hh_id) \u2014 ensures calibration consistency" + }, + { + "id": "sparse_build", + "label": "Sparse Matrix Construction", + "node_type": "process", + "description": "COO triples \u2192 CSR matrix \u2014 shape (n_targets, 5.16M), ~0.02% nonzero" + }, + { + "id": "out_pkg", + "label": "calibration_package.pkl", + "node_type": "output", + "description": "X_sparse CSR matrix, targets_df, initial_weights, metadata" + }, + { + "id": "util_sql", + "label": "sqlalchemy", + "node_type": "utility", + "description": "Database queries" + }, + { + "id": "util_pool", + "label": "ProcessPoolExecutor", + "node_type": "utility", + "description": "Parallel state computation" + }, + { + "id": "util_takeup_s5", + "label": "compute_block_takeup_for_entities()", + "node_type": "utility", + "description": "utils/takeup.py" + }, + { + "id": "util_scipy", + "label": "scipy.sparse", + "node_type": "utility", + "description": "CSR/COO matrix construction" + } + ], + "edges": [ + { + "source": "in_cps_s5", + "target": "target_resolve", + "edge_type": "data_flow" + }, + { + "source": "in_db_s5", + "target": "target_resolve", + "edge_type": "external_source", + "label": "SQL targets" + }, + { + "source": "in_config_s5", + "target": "target_resolve", + "edge_type": "data_flow", + "label": "include list" + }, + { + "source": "target_resolve", + "target": "target_uprate", + "edge_type": "data_flow" + }, + { + "source": "target_uprate", + "target": "geo_build", + "edge_type": "data_flow" + }, + { + "source": "geo_build", + "target": "constraint_resolve", + "edge_type": "data_flow" + }, + { + "source": "constraint_resolve", + "target": "state_precomp", + "edge_type": "data_flow" + }, + { + "source": "in_cps_s5", + "target": "state_precomp", + "edge_type": "data_flow", + "label": "household data" + }, + { + "source": "state_precomp", + "target": "clone_assembly", + "edge_type": "data_flow" + }, + { + "source": "in_blocks_s5", + "target": "clone_assembly", + "edge_type": "data_flow", + "label": "block populations" + }, + { + "source": "clone_assembly", + "target": "takeup_rerand", + "edge_type": "data_flow" + }, + { + "source": "takeup_rerand", + "target": "sparse_build", + "edge_type": "data_flow" + }, + { + "source": "sparse_build", + "target": "out_pkg", + "edge_type": "produces_artifact" + }, + { + "source": "util_sql", + "target": "target_resolve", + "edge_type": "uses_utility" + }, + { + "source": "util_pool", + "target": "state_precomp", + "edge_type": "uses_utility" + }, + { + "source": "util_takeup_s5", + "target": "takeup_rerand", + "edge_type": "uses_utility" + }, + { + "source": "util_scipy", + "target": "sparse_build", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 6, + "label": "Stage 6", + "title": "Stage 6: Weight Fitting (L0 Calibration)", + "description": "Fit log-weights using L0 HardConcrete gates on GPU", + "country": "us", + "nodes": [ + { + "id": "in_pkg_s6", + "label": "calibration_package.pkl", + "node_type": "input", + "description": "From Stage 5 \u2014 X_sparse, targets_df, initial_weights" + }, + { + "id": "modal_gpu", + "label": "Modal GPU Container", + "node_type": "external", + "description": "T4 / A10 / A100 / H100 \u2014 32GB RAM, 8 CPU" + }, + { + "id": "init_weights", + "label": "Compute Initial Weights", + "node_type": "process", + "description": "Population-proportional per CD" + }, + { + "id": "create_model", + "label": "Create SparseCalibrationWeights", + "node_type": "process", + "description": "n_features = 5.16M, init_keep_prob = 0.999" + }, + { + "id": "fit_model", + "label": "model.fit()", + "node_type": "process", + "description": "Adam optimizer \u2014 loss = RSE + \u03bb\u2080\u00b7L0(w) + \u03bb\u2082\u00b7\u2016w\u2016\u00b2" + }, + { + "id": "extract_weights", + "label": "Extract Weights", + "node_type": "process", + "description": "Deterministic gate threshold \u2014 produces exact zeros" + }, + { + "id": "out_weights", + "label": "calibration_weights.npy", + "node_type": "output", + "description": "Shape: (n_records \u00d7 n_clones) \u2014 most entries zero" + }, + { + "id": "out_geo_s6", + "label": "geography.npz", + "node_type": "output", + "description": "block_geoid, cd_geoid, county_fips, state_fips" + }, + { + "id": "out_diag", + "label": "unified_diagnostics.csv", + "node_type": "output", + "description": "Per-target error analysis" + }, + { + "id": "out_config_s6", + "label": "unified_run_config.json", + "node_type": "output", + "description": "Hyperparameters + SHA256 checksums" + }, + { + "id": "util_l0", + "label": "l0-python", + "node_type": "utility", + "description": "SparseCalibrationWeights \u2014 HardConcrete gates" + }, + { + "id": "util_pytorch", + "label": "PyTorch", + "node_type": "utility", + "description": "Adam optimizer, autograd, CUDA" + } + ], + "edges": [ + { + "source": "in_pkg_s6", + "target": "init_weights", + "edge_type": "data_flow" + }, + { + "source": "init_weights", + "target": "create_model", + "edge_type": "data_flow" + }, + { + "source": "create_model", + "target": "fit_model", + "edge_type": "data_flow" + }, + { + "source": "modal_gpu", + "target": "fit_model", + "edge_type": "runs_on_infra", + "label": "runs on" + }, + { + "source": "fit_model", + "target": "extract_weights", + "edge_type": "data_flow" + }, + { + "source": "extract_weights", + "target": "out_weights", + "edge_type": "produces_artifact" + }, + { + "source": "extract_weights", + "target": "out_geo_s6", + "edge_type": "produces_artifact" + }, + { + "source": "fit_model", + "target": "out_diag", + "edge_type": "produces_artifact" + }, + { + "source": "fit_model", + "target": "out_config_s6", + "edge_type": "produces_artifact" + }, + { + "source": "util_l0", + "target": "create_model", + "edge_type": "uses_utility" + }, + { + "source": "util_pytorch", + "target": "fit_model", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 7, + "label": "Stage 7", + "title": "Stage 7: Local Area H5 Build", + "description": "Build 51 state + 435 district + 1 city H5 files on Modal workers", + "country": "us", + "nodes": [ + { + "id": "in_weights_s7", + "label": "calibration_weights.npy", + "node_type": "input", + "description": "From Stage 6" + }, + { + "id": "in_dataset_s7", + "label": "source_imputed_stratified_extended_cps.h5", + "node_type": "input", + "description": "From Stage 4" + }, + { + "id": "in_db_s7", + "label": "policy_data.db", + "node_type": "external", + "description": "CD list for partitioning" + }, + { + "id": "modal_coord", + "label": "Modal Coordinator", + "node_type": "external", + "description": "coordinate_publish() \u2014 8GB RAM, 24hr timeout" + }, + { + "id": "partition", + "label": "Partition Work", + "node_type": "process", + "description": "50 workers \u2014 weights: state by CD count, city=11, district=1" + }, + { + "id": "phase1", + "label": "Phase 1: States", + "node_type": "us_specific", + "description": "51 state H5 files (50 + DC) \u2014 workers in parallel" + }, + { + "id": "phase2", + "label": "Phase 2: Districts", + "node_type": "us_specific", + "description": "~435 congressional district H5 files \u2014 workers in parallel" + }, + { + "id": "phase3", + "label": "Phase 3: Cities", + "node_type": "us_specific", + "description": "NYC (5 counties, 13 CDs) \u2014 city probability filtering" + }, + { + "id": "build_h5", + "label": "build_h5() \u2014 Core Logic", + "node_type": "process", + "description": "18-step H5 construction \u2014 load, reshape, filter, clone, derive geo, SPM, takeup, write" + }, + { + "id": "worker_s7", + "label": "Modal Worker Container", + "node_type": "external", + "description": "16GB RAM, 1 CPU each, 8-hour timeout" + }, + { + "id": "geo_derive", + "label": "derive_geography_from_blocks()", + "node_type": "process", + "description": "15 geographic variables from block GEOID" + }, + { + "id": "spm_recalc", + "label": "SPM Threshold Recalculation", + "node_type": "process", + "description": "Local median rents, family composition, tenure type" + }, + { + "id": "takeup_apply", + "label": "Takeup Re-application", + "node_type": "process", + "description": "9 takeup variables \u2014 block-level seeded draws" + }, + { + "id": "out_states", + "label": "states/*.h5", + "node_type": "output", + "description": "51 files \u2014 AL.h5 through WY.h5" + }, + { + "id": "out_districts", + "label": "districts/*.h5", + "node_type": "output", + "description": "~435 files \u2014 NC-01.h5, CA-52.h5, ..." + }, + { + "id": "out_cities", + "label": "cities/*.h5", + "node_type": "output", + "description": "1 file \u2014 NYC.h5" + }, + { + "id": "out_manifest", + "label": "manifest.json", + "node_type": "output", + "description": "SHA256 checksums for all H5 files" + }, + { + "id": "util_build_h5", + "label": "publish_local_area.build_h5()", + "node_type": "utility", + "description": "calibration/publish_local_area.py" + }, + { + "id": "util_takeup_s7", + "label": "apply_block_takeup_to_arrays()", + "node_type": "utility", + "description": "utils/takeup.py" + } + ], + "edges": [ + { + "source": "in_weights_s7", + "target": "partition", + "edge_type": "data_flow" + }, + { + "source": "in_dataset_s7", + "target": "partition", + "edge_type": "data_flow" + }, + { + "source": "in_db_s7", + "target": "partition", + "edge_type": "external_source", + "label": "CD list" + }, + { + "source": "partition", + "target": "phase1", + "edge_type": "data_flow" + }, + { + "source": "phase1", + "target": "phase2", + "edge_type": "data_flow" + }, + { + "source": "phase2", + "target": "phase3", + "edge_type": "data_flow" + }, + { + "source": "phase1", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "phase2", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "phase3", + "target": "build_h5", + "edge_type": "data_flow", + "label": "calls" + }, + { + "source": "build_h5", + "target": "geo_derive", + "edge_type": "data_flow" + }, + { + "source": "geo_derive", + "target": "spm_recalc", + "edge_type": "data_flow" + }, + { + "source": "spm_recalc", + "target": "takeup_apply", + "edge_type": "data_flow" + }, + { + "source": "modal_coord", + "target": "worker_s7", + "edge_type": "runs_on_infra", + "label": "orchestrates" + }, + { + "source": "worker_s7", + "target": "build_h5", + "edge_type": "runs_on_infra", + "label": "runs" + }, + { + "source": "phase1", + "target": "out_states", + "edge_type": "produces_artifact" + }, + { + "source": "phase2", + "target": "out_districts", + "edge_type": "produces_artifact" + }, + { + "source": "phase3", + "target": "out_cities", + "edge_type": "produces_artifact" + }, + { + "source": "build_h5", + "target": "out_manifest", + "edge_type": "produces_artifact" + }, + { + "source": "util_build_h5", + "target": "build_h5", + "edge_type": "uses_utility" + }, + { + "source": "util_takeup_s7", + "target": "takeup_apply", + "edge_type": "uses_utility" + } + ] + }, + { + "id": 8, + "label": "Stage 8", + "title": "Stage 8: Validation & Promotion", + "description": "7-layer validation, staging upload, atomic promotion to production", + "country": "us", + "nodes": [ + { + "id": "in_h5s", + "label": "51 state + 435 district + 1 city H5s", + "node_type": "input", + "description": "From Stage 7 (Modal volume)" + }, + { + "id": "in_db_s8", + "label": "policy_data.db", + "node_type": "external", + "description": "Validation targets" + }, + { + "id": "v1", + "label": "Layer 1: Manifest Verification", + "node_type": "process", + "description": "SHA256 checksums per file \u2014 catches corruption" + }, + { + "id": "v2", + "label": "Layer 2: Structural Sanity", + "node_type": "process", + "description": "Weight non-negativity, entity ID uniqueness, no NaN/Inf, mapping integrity" + }, + { + "id": "v3", + "label": "Layer 3: Target-Based Validation", + "node_type": "process", + "description": "Full Microsimulation per area \u2014 compare sim values to targets" + }, + { + "id": "v4", + "label": "Layer 4: Smoke Test", + "node_type": "process", + "description": "Sum 20 key vars across 51 states \u2014 GDP ~$29T, pop ~335M" + }, + { + "id": "v5", + "label": "Layer 5: National H5 Validation", + "node_type": "process", + "description": ">30% deviation flagged \u2014 hardcoded reference values" + }, + { + "id": "v6", + "label": "Layer 6: Pre-Upload Validation", + "node_type": "process", + "description": "File size minimums (100MB/50MB), H5 structure, Microsim aggregates" + }, + { + "id": "v7", + "label": "Layer 7: Package Validation", + "node_type": "process", + "description": "Matrix achievability, target ratio analysis, zero-row detection" + }, + { + "id": "staging_upload", + "label": "Upload to Staging", + "node_type": "process", + "description": "upload_to_staging_hf() \u2014 batches of 50 files/commit" + }, + { + "id": "atomic_promote", + "label": "Atomic Promotion", + "node_type": "process", + "description": "promote_staging_to_production_hf() \u2014 single CommitOperationCopy commit" + }, + { + "id": "gcs_upload", + "label": "GCS Parallel Upload", + "node_type": "external", + "description": "gs://policyengine-us-data/ \u2014 version metadata on each blob" + }, + { + "id": "staging_cleanup", + "label": "Staging Cleanup", + "node_type": "process", + "description": "cleanup_staging_hf() \u2014 removes staging/ files" + }, + { + "id": "out_hf_prod", + "label": "HuggingFace Production", + "node_type": "external", + "description": "policyengine/policyengine-us-data \u2014 final published datasets" + }, + { + "id": "out_gcs", + "label": "Google Cloud Storage", + "node_type": "external", + "description": "gs://policyengine-us-data/ \u2014 CDN/backup" + }, + { + "id": "util_manifest_s8", + "label": "manifest.py", + "node_type": "utility", + "description": "generate_manifest(), verify_manifest()" + }, + { + "id": "util_sanity", + "label": "sanity_checks.py", + "node_type": "utility", + "description": "run_sanity_checks()" + }, + { + "id": "util_validate", + "label": "validate_staging.py", + "node_type": "utility", + "description": "Full target comparison" + }, + { + "id": "util_upload", + "label": "data_upload.py", + "node_type": "utility", + "description": "staging/promote/cleanup" + } + ], + "edges": [ + { + "source": "in_h5s", + "target": "v1", + "edge_type": "data_flow" + }, + { + "source": "in_db_s8", + "target": "v3", + "edge_type": "external_source", + "label": "targets" + }, + { + "source": "v1", + "target": "v2", + "edge_type": "data_flow" + }, + { + "source": "v2", + "target": "v3", + "edge_type": "data_flow" + }, + { + "source": "v3", + "target": "v4", + "edge_type": "data_flow" + }, + { + "source": "v4", + "target": "v5", + "edge_type": "data_flow" + }, + { + "source": "v5", + "target": "v6", + "edge_type": "data_flow" + }, + { + "source": "v6", + "target": "v7", + "edge_type": "data_flow" + }, + { + "source": "v7", + "target": "staging_upload", + "edge_type": "data_flow", + "label": "all pass" + }, + { + "source": "staging_upload", + "target": "atomic_promote", + "edge_type": "data_flow" + }, + { + "source": "atomic_promote", + "target": "gcs_upload", + "edge_type": "data_flow" + }, + { + "source": "gcs_upload", + "target": "staging_cleanup", + "edge_type": "data_flow" + }, + { + "source": "atomic_promote", + "target": "out_hf_prod", + "edge_type": "produces_artifact" + }, + { + "source": "gcs_upload", + "target": "out_gcs", + "edge_type": "produces_artifact" + }, + { + "source": "util_manifest_s8", + "target": "v1", + "edge_type": "uses_utility" + }, + { + "source": "util_sanity", + "target": "v2", + "edge_type": "uses_utility" + }, + { + "source": "util_validate", + "target": "v3", + "edge_type": "uses_utility" + }, + { + "source": "util_upload", + "target": "staging_upload", + "edge_type": "uses_utility" + } + ] + } + ], + "metadata": { + "generated_at": "2026-04-06T18:47:05.119469+00:00", + "total_nodes": 157, + "total_edges": 160 + } +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/postcss.config.mjs b/docs/pipeline-diagrams/postcss.config.mjs new file mode 100644 index 000000000..c2ddf7482 --- /dev/null +++ b/docs/pipeline-diagrams/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/docs/pipeline-diagrams/public/dots/uk/overview.dot b/docs/pipeline-diagrams/public/dots/uk/overview.dot new file mode 100644 index 000000000..c438638cc --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/overview.dot @@ -0,0 +1,25 @@ +digraph "UK pipeline overview" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + stage0 [label=<
Stage 0: Raw data download
FRS, SPI, WAS, LCFS, ETB
UK Data Service (FRS)
HMRC (SPI), ONS (WAS, LCFS, ETB)
NEED 2023, Advani-Summers CG
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + stage1 [label=<
Stage 1: Base dataset construction
FRS parsing + SPI parsing
FRS: IDs, demographics, income, benefits
Housing, pensions, disability, takeup
SPI: HMRC field mapping, age imputation
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage2 [label=<
Stage 2: Income enhancement
SPI synthetic high-earners
Zero-weight copies with SPI incomes
Dividend imputation on original
Stack original + synthetic
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage3_absent [label=<
Stage 3: Stratification
Not applicable — UK calibrates full FRS
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + stage4 [label=<
Stage 4: Source imputation
WAS + LCFS + ETB + CG + SS + SL
Wealth (WAS), Consumption (LCFS)
VAT, Public services (ETB)
Capital gains, Salary sacrifice
Student loan plans
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage5 [label=<
Stage 5: Matrix build
22 target source modules
OBR, ONS, DWP, HMRC, VOA
NTS, SLC, Scottish Gov
National + region + country targets
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage6 [label=<
Stage 6: Weight fitting
Torch Adam optimizer (SRE loss)
650 constituency weights
512 epochs, lr=0.1
Uprate to 2025 first
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage7 [label=<
Stage 7: Local area calibration
360 local authorities
Same Torch optimizer framework
LA-specific targets
Boundary change handling
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage8 [label=<
Stage 8: Validation & output
4 H5 files + 2 weight files
Downrate to 2023
Tiny dataset creation (1K HH)
HuggingFace upload
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + + stage0 -> stage1 [color="#334155" penwidth=2 style="solid" label="raw files" fontcolor="#334155"] + stage1 -> stage2 [color="#334155" penwidth=2 style="solid" label="frs_2023_24.h5 + SPI" fontcolor="#334155"] + stage2 -> stage3_absent [color="#9ca3af" penwidth=1 style="dotted"] + stage3_absent -> stage4 [color="#9ca3af" penwidth=1 style="dotted"] + stage2 -> stage4 [color="#334155" penwidth=2 style="solid" label="enhanced FRS" fontcolor="#334155"] + stage4 -> stage5 [color="#334155" penwidth=2 style="solid" label="fully imputed" fontcolor="#334155"] + stage5 -> stage6 [color="#334155" penwidth=2 style="solid" label="target matrix" fontcolor="#334155"] + stage6 -> stage7 [color="#334155" penwidth=2 style="solid" label="constituency weights" fontcolor="#334155"] + stage7 -> stage8 [color="#16a34a" penwidth=2 style="solid" label="calibrated dataset" fontcolor="#16a34a"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_0.dot b/docs/pipeline-diagrams/public/dots/uk/stage_0.dot new file mode 100644 index 000000000..d5dba6492 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_0.dot @@ -0,0 +1,37 @@ +digraph "Stage 0: Raw data download" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + src_frs [label=<
UK Data Service
Family Resources Survey 2023-24
14 tab-delimited files
adult.tab, child.tab, benunit.tab...
Requires UKDS access
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_spi [label=<
HMRC
Survey of Personal Incomes 2020-21
Tab-delimited file
High-income tax records
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_was [label=<
ONS
Wealth and Assets Survey 2006-20
Round 7 (multiple waves)
Household wealth data
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_lcfs [label=<
ONS
Living Costs and Food Survey 2021-22
Household + person tables
Expenditure categories
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_etb [label=<
ONS
Effects of Taxes & Benefits 1977-2021
VAT, public services
NHS, education, transport
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_need [label=<
DESNZ
NEED 2023
National Energy Efficiency Data
kWh by income band / tenure / region
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_cg [label=<
Advani-Summers
Capital gains distribution
Percentiles by income band
External CSV
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + dl_frs [label=<
Download FRS
14 tab files to frs_2023_24/
adult, child, benunit, househol
pension, job, oddjob, accounts
benefits, maint, penprov, chldcare
extchild, mortgage
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + dl_spi [label=<
Download SPI
spi_2020_21/ folder
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + dl_was [label=<
Download WAS
was_2006_20/ folder
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + dl_lcfs [label=<
Download LCFS
lcfs_2021_22/ folder
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + dl_etb [label=<
Download ETB
etb_1977_21/ folder
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_frs [label=<
frs_2023_24/
Raw tab files on disk
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_spi [label=<
spi_2020_21/
Raw tab files on disk
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_surveys [label=<
was/, lcfs/, etb/
Donor survey folders
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_static [label=<
Static data files
In storage/ directory
capital_gains_distribution.csv.gz
lha_list_of_rents.csv.gz
uprating_factors.csv
tax_benefit.csv, demographics.csv
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + absent_census [label=<
Census Bureau CPS download
US uses Census ASEC microdata
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_puf [label=<
IRS PUF download
US uses IRS Public Use File
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + src_frs -> dl_frs [color="#b45309" penwidth=1.5 style="dotted"] + src_spi -> dl_spi [color="#b45309" penwidth=1.5 style="dotted"] + src_was -> dl_was [color="#b45309" penwidth=1.5 style="dotted"] + src_lcfs -> dl_lcfs [color="#b45309" penwidth=1.5 style="dotted"] + src_etb -> dl_etb [color="#b45309" penwidth=1.5 style="dotted"] + dl_frs -> out_frs [color="#16a34a" penwidth=2 style="solid"] + dl_spi -> out_spi [color="#16a34a" penwidth=2 style="solid"] + dl_was -> out_surveys [color="#16a34a" penwidth=2 style="solid"] + dl_lcfs -> out_surveys [color="#16a34a" penwidth=2 style="solid"] + dl_etb -> out_surveys [color="#16a34a" penwidth=2 style="solid"] + src_need -> out_static [color="#b45309" penwidth=1.5 style="dotted"] + src_cg -> out_static [color="#b45309" penwidth=1.5 style="dotted"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_1.dot b/docs/pipeline-diagrams/public/dots/uk/stage_1.dot new file mode 100644 index 000000000..c518b09b7 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_1.dot @@ -0,0 +1,52 @@ +digraph "Stage 1: Base dataset construction" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_frs_raw [label=<
frs_2023_24/
14 raw tab files from Stage 0
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + load_tables [label=<
Load & merge tables
Similar to US census_cps parsing
Merge adult + child → person
Read benunit, househol, pension
job, oddjob, accounts, benefits
maint, penprov, chldcare, mortgage
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_ids [label=<
add_id_variables()
household_id, benunit_id, person_id
household_id from SERNUM
benunit_id = hh_id * 100 + index
person_id = hh_id * 1000 + index
Same pattern as US add_id_variables()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_hh [label=<
add_household_variables()
Region, tenure, accommodation
12 regions from GVTREGNO
6 tenure types (council, HA, private...)
7 accommodation types
Council tax imputation
Domestic rates (NI handling)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_personal [label=<
add_personal_variables()
age, gender, education, employment
Age from AGE80 + AGE
12 employment status categories
6 marital status categories
8 current education levels
22+ highest qualification codes
Similar to US add_personal_variables()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_income [label=<
add_income_variables()
Annualized (x 52.18 weeks)
Employment (INEARNS)
Self-employment (SEINCAM2)
Private pension (PENPAY + PTAMT)
Savings interest by account type
Dividends (tax-adjusted)
Property, maintenance, misc
Similar to US add_personal_income_variables()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_benefits [label=<
add_benefit_variables()
20 UK benefit types
Universal Credit, JSA, ESA
PIP, DLA, Carer's Allowance
Child Benefit, Tax Credits
State Pension, Pension Credit
Housing Benefit, Income Support
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + add_housing [label=<
add_housing_variables()
Rent, mortgage, council tax
HHRENT, MORTINT
Mortgage capital repayment
Water & sewerage charges
Service charges, insurance
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_pensions [label=<
add_pension_variables()
Personal + employer contributions
Personal pension (clipped p95)
Employee pension (DEDUC1)
Employer pension (3x employee est.)
Salary sacrifice (SPNAMT from job)
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + add_disability [label=<
add_disability_indicators()
DLA/PIP-based classifications
is_disabled_for_benefits
is_enhanced_disabled
is_severely_disabled
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + add_takeup [label=<
add_stochastic_takeup()
10+ benefit programs
Child Benefit, UC, Pension Credit
Marriage Allowance, Tax-Free Childcare
Scottish Child Payment (97% / 85%)
Rates from YAML parameters
Similar to US add_takeup()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_brma [label=<
assign_brma()
Broad Rental Market Areas
Random sample within region
Weighted by LHA observation count
Determines housing allowance rates
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + add_stochastic [label=<
add_stochastic_variables()
RNG seed=100
TV ownership & licence evasion
First-time buyer (3.85%)
Private school random draw
Childcare hours (normal dist.)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_frs [label=<
frs_2023_24.h5
Base FRS dataset
~20K households, person/benunit/hh
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + in_spi_raw [label=<
spi_2020_21/
Raw SPI tab files
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + spi_parse [label=<
create_spi()
HMRC field mapping
DIVIDENDS, GIFTAID, INCBBS
INCPROP, PAY, PENSION, PROFITS
Similar to US preprocess_puf()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + spi_age [label=<
Age imputation
AGERANGE → random within bucket
7-8 age range buckets (16-90)
Similar to US impute_missing_demographics()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + spi_region [label=<
Region mapping
12 regions from GORCODE
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_spi [label=<
SPI dataset
Person-level tax records
Weight from FACT field
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_datasets [label=<
utils/datasets.py
sum_to_entity(), categorical()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_takeup_params [label=<
parameters/take_up/
YAML take-up rate files
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_stochastic_params [label=<
parameters/stochastic/
TV, first-time buyer YAML
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_ssn [label=<
add_ssn_card_type()
US immigration classification (13M undocumented)
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_spm [label=<
add_spm_variables()
US SPM thresholds + transfers
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_prev_year [label=<
add_previous_year_income()
US cross-year PERIDNUM linking
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_frs_raw -> load_tables [color="#334155" penwidth=2 style="solid"] + load_tables -> add_ids [color="#334155" penwidth=2 style="solid"] + add_ids -> add_hh [color="#334155" penwidth=2 style="solid"] + add_hh -> add_personal [color="#334155" penwidth=2 style="solid"] + add_personal -> add_income [color="#334155" penwidth=2 style="solid"] + add_income -> add_benefits [color="#334155" penwidth=2 style="solid"] + add_benefits -> add_housing [color="#334155" penwidth=2 style="solid"] + add_housing -> add_pensions [color="#334155" penwidth=2 style="solid"] + add_pensions -> add_disability [color="#334155" penwidth=2 style="solid"] + add_disability -> add_takeup [color="#334155" penwidth=2 style="solid"] + add_takeup -> add_brma [color="#334155" penwidth=2 style="solid"] + add_brma -> add_stochastic [color="#334155" penwidth=2 style="solid"] + add_stochastic -> out_frs [color="#16a34a" penwidth=2 style="solid" label="frs_2023_24.h5" fontcolor="#16a34a"] + in_spi_raw -> spi_parse [color="#334155" penwidth=2 style="solid"] + spi_parse -> spi_age [color="#334155" penwidth=2 style="solid"] + spi_age -> spi_region [color="#334155" penwidth=2 style="solid"] + spi_region -> out_spi [color="#16a34a" penwidth=2 style="solid"] + util_datasets -> load_tables [color="#7c3aed" penwidth=1.5 style="dashed"] + util_takeup_params -> add_takeup [color="#7c3aed" penwidth=1.5 style="dashed"] + util_stochastic_params -> add_stochastic [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_2.dot b/docs/pipeline-diagrams/public/dots/uk/stage_2.dot new file mode 100644 index 000000000..1ef0c3e6c --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_2.dot @@ -0,0 +1,32 @@ +digraph "Stage 2: Income enhancement (SPI synthetic)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_frs [label=<
frs_2023_24.h5
From Stage 1
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_spi [label=<
SPI 2020-21
From Stage 1
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + generate_spi_table [label=<
generate_spi_table()
100K weighted sample from SPI
Map AGERANGE → ages
Map region codes
6 income types as predictors
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + train_income_qrf [label=<
Train income QRF
Predictors: age, gender, region
employment_income
self_employment_income
savings_interest_income
dividend_income
private_pension_income
property_income
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + create_copy [label=<
Create zero-weight copy
Subsample 10K households
household_weight = 1 (zero-weight)
Full income imputation on copy
Similar to US PUF clone approach
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + impute_dividends [label=<
Impute dividends on original
Only dividend_income updated
Preserves all other FRS incomes
Addresses FRS dividend undercount
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stack [label=<
stack_datasets()
Combine original + synthetic
Offsets IDs to avoid collisions
Similar to US extended_cps stacking
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + adjust_housing [label=<
Adjust housing costs
Proportional rent/mortgage scaling
Maintains consistency with
new income distribution
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_enhanced [label=<
Enhanced FRS (income-augmented)
Original + synthetic high-earners
Larger dataset with top-income tail
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_qrf [label=<
microimpute QRF
fit_predict() for imputation
Same library as US pipeline
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_stack [label=<
utils/stack.py
stack_datasets()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_puf_blend [label=<
PUF variable blending
US blends IRS PUF tax variables onto CPS
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_qbi [label=<
QBI simulation
US W-2 wages, UBIA, SSTB from PUF
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_spi -> generate_spi_table [color="#334155" penwidth=2 style="solid"] + generate_spi_table -> train_income_qrf [color="#334155" penwidth=2 style="solid"] + in_frs -> create_copy [color="#334155" penwidth=2 style="solid"] + train_income_qrf -> create_copy [color="#334155" penwidth=2 style="solid" label="income.pkl" fontcolor="#334155"] + in_frs -> impute_dividends [color="#334155" penwidth=2 style="solid"] + train_income_qrf -> impute_dividends [color="#334155" penwidth=2 style="solid"] + create_copy -> stack [color="#334155" penwidth=2 style="solid" label="synthetic" fontcolor="#334155"] + impute_dividends -> stack [color="#334155" penwidth=2 style="solid" label="original" fontcolor="#334155"] + stack -> adjust_housing [color="#334155" penwidth=2 style="solid"] + adjust_housing -> out_enhanced [color="#16a34a" penwidth=2 style="solid"] + util_qrf -> train_income_qrf [color="#7c3aed" penwidth=1.5 style="dashed"] + util_stack -> stack [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_3.dot b/docs/pipeline-diagrams/public/dots/uk/stage_3.dot new file mode 100644 index 000000000..a7a16482a --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_3.dot @@ -0,0 +1,10 @@ +digraph "Stage 3: Stratification (absent in UK)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + absent_stratification [label=<
Stratified subsampling
US stratifies CPS to ~12K HH for calibration efficiency
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_clone [label=<
Clone-based geography assignment
US creates 430 clones with different geographies
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + note [label=<
Not applicable to UK pipeline
UK calibrates full FRS directly
UK FRS has ~20K households
No subsampling needed before calibration
Geography assigned via BRMA in Stage 1
Calibration (Stage 6-7) operates on full dataset
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_4.dot b/docs/pipeline-diagrams/public/dots/uk/stage_4.dot new file mode 100644 index 000000000..26c666914 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_4.dot @@ -0,0 +1,52 @@ +digraph "Stage 4: Source imputation (WAS + LCFS + ETB + CG)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_frs [label=<
Enhanced FRS
From Stage 2 (income-augmented)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_was [label=<
WAS 2006-20
Wealth and Assets Survey
Round 7 (multiple waves)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_lcfs [label=<
LCFS 2021-22
Living Costs and Food Survey
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_etb [label=<
ETB 1977-2021
Effects of Taxes & Benefits
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_need [label=<
NEED 2023
Energy efficiency data
kWh by income/tenure/region
Ofgem Q2 2026 unit rates
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_cg [label=<
Advani-Summers CG dist.
Capital gains percentiles
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + wealth_train [label=<
generate_was_table()
20+ WAS fields renamed
corporate_wealth = pensions + shares
NI → Wales mapping (WAS gap)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + wealth_impute [label=<
impute_wealth()
QRF: 10 predictors → 10 targets
owned_land, property_wealth
corporate_wealth, savings
main_residence_value
gross/net_financial_wealth
num_vehicles (NTS 2024 calibrated)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + consumption_train [label=<
generate_lcfs_table()
12 COICOP categories + energy
P601-P612 consumption categories
Electricity / gas separation
B226/B489/B490 interview vars
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + energy_calibrate [label=<
NEED 2023 energy calibration
Scale to admin kWh data
10 income bands, 3 tenure types
5 property types, 10 regions
Elec: 24.67p/kWh, Gas: 5.74p/kWh
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + consumption_impute [label=<
impute_consumption()
QRF: 15 consumption categories
Food, alcohol, clothing, housing
Health, transport, communication
Recreation, education, restaurants
Electricity, gas (NEED-calibrated)
Petrol/diesel (NTS fuel model)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + vat_impute [label=<
impute_vat()
QRF from ETB 2020
VAT expenditure rate estimation
Accounts for 3% reduced rate
Backs out from discretionary spend
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + services_impute [label=<
impute_services()
NHS + education + transport
A&E, admitted, outpatient visits
DfE education spending
Rail subsidy (usage × fare index)
Bus subsidy spending
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + cg_impute [label=<
impute_capital_gains()
Torch optimization + spline
Stack dataset (double households)
Optimize blend factor (sigmoid + Adam)
Spline interpolation of percentiles
Update household weights
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + ss_impute [label=<
impute_salary_sacrifice()
2-stage QRF + headcount
Stage 1: QRF for non-respondents
Stage 2: Convert pension → SS
Target: 5.4M below-cap users
Based on FRS SALSAC field
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + sl_impute [label=<
impute_student_loan_plan()
Age-based plan assignment
Plan 1: start < 2012
Plan 2: 2012-2023
Plan 5: 2023+
Plan 4: Scottish
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + out_imputed [label=<
Enhanced FRS (fully imputed)
All imputations applied
Wealth, consumption, VAT
Services, capital gains
Salary sacrifice, student loans
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_qrf [label=<
microimpute QRF
fit_predict() for all imputations
Same library as US pipeline
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_torch [label=<
PyTorch
Capital gains optimization
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_acs [label=<
ACS rent/real_estate_taxes QRF
US imputes from American Community Survey
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_sipp [label=<
SIPP tips/assets QRF
US imputes from Survey of Income & Program Participation
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_scf [label=<
SCF net_worth/auto_loan QRF
US imputes from Survey of Consumer Finances
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_frs -> wealth_impute [color="#334155" penwidth=2 style="solid"] + wealth_impute -> consumption_impute [color="#334155" penwidth=2 style="solid"] + consumption_impute -> vat_impute [color="#334155" penwidth=2 style="solid"] + vat_impute -> services_impute [color="#334155" penwidth=2 style="solid"] + services_impute -> cg_impute [color="#334155" penwidth=2 style="solid"] + cg_impute -> ss_impute [color="#334155" penwidth=2 style="solid"] + ss_impute -> sl_impute [color="#334155" penwidth=2 style="solid"] + sl_impute -> out_imputed [color="#16a34a" penwidth=2 style="solid"] + in_was -> wealth_train [color="#334155" penwidth=2 style="solid"] + wealth_train -> wealth_impute [color="#334155" penwidth=2 style="solid" label="wealth.pkl" fontcolor="#334155"] + in_lcfs -> consumption_train [color="#334155" penwidth=2 style="solid"] + consumption_train -> consumption_impute [color="#334155" penwidth=2 style="solid" label="consumption.pkl" fontcolor="#334155"] + in_need -> energy_calibrate [color="#b45309" penwidth=1.5 style="dotted"] + energy_calibrate -> consumption_impute [color="#334155" penwidth=2 style="solid" label="NEED targets" fontcolor="#334155"] + in_etb -> vat_impute [color="#334155" penwidth=2 style="solid" label="vat.pkl" fontcolor="#334155"] + in_etb -> services_impute [color="#334155" penwidth=2 style="solid" label="services.pkl" fontcolor="#334155"] + in_cg -> cg_impute [color="#b45309" penwidth=1.5 style="dotted" label="percentiles" fontcolor="#b45309"] + util_qrf -> wealth_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> consumption_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> vat_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> services_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> ss_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_torch -> cg_impute [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_5.dot b/docs/pipeline-diagrams/public/dots/uk/stage_5.dot new file mode 100644 index 000000000..73c4cf05a --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_5.dot @@ -0,0 +1,34 @@ +digraph "Stage 5: Matrix build (calibration target construction)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_frs [label=<
Enhanced FRS (fully imputed)
From Stage 4
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + src_obr [label=<
OBR Forecasts
Tax/benefit projections
Income tax, NICs, CGT
Corporation tax, VAT
Benefit spending forecasts
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_ons [label=<
ONS Statistics
Demographics + households
Population by age/gender
Household counts by type
Tenure distribution
Land values, savings rates
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_dwp [label=<
DWP Statistics
Benefit admin data
PIP caseloads
UC payment distribution
Benefit cap counts
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_hmrc [label=<
HMRC SPI / ASHE
Income + salary sacrifice
Income distribution by band
Salary sacrifice headcount
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + src_other [label=<
Other sources
VOA, NTS, SLC, Scottish Gov
Council tax bands (VOA)
Vehicle ownership (NTS)
Student loans (SLC)
Scottish demographics
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + registry [label=<
Target registry
discover_source_modules()
Auto-imports 22 source modules
get_all_targets(year, level)
De-duplication by name
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + resolve [label=<
Target resolution
National + region + country
Exact year or extrapolation
Geographic level filtering
Similar to US target resolution
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + compute [label=<
Compute target columns
targets/compute/ modules
income.py: income bands
demographics.py: age/gender
benefits.py: UC, PIP, benefit cap
households.py: structure/type
council_tax.py: CT bands
other.py: vehicles, savings
40+ compute functions
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + build_matrix [label=<
create_target_matrix()
build_loss_matrix.py
DataFrame: households × targets
Each column = indicator vector
Similar to US sparse matrix build
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_matrix [label=<
Target matrix + targets series
(df, targets_series) tuple
Matrix for calibration loss
Used by Stage 6 optimizer
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_schema [label=<
targets/schema.py
Target, GeographicLevel, Unit
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_sources_yaml [label=<
sources.yaml
URL mappings for data sources
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_sql [label=<
SQLite policy_data.db
US uses 10-step ETL into SQL database
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_sparse [label=<
COO → CSR sparse matrix
US uses scipy.sparse for 5.16M columns
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + src_obr -> registry [color="#b45309" penwidth=1.5 style="dotted"] + src_ons -> registry [color="#b45309" penwidth=1.5 style="dotted"] + src_dwp -> registry [color="#b45309" penwidth=1.5 style="dotted"] + src_hmrc -> registry [color="#b45309" penwidth=1.5 style="dotted"] + src_other -> registry [color="#b45309" penwidth=1.5 style="dotted"] + registry -> resolve [color="#334155" penwidth=2 style="solid"] + resolve -> compute [color="#334155" penwidth=2 style="solid"] + in_frs -> compute [color="#334155" penwidth=2 style="solid" label="household data" fontcolor="#334155"] + compute -> build_matrix [color="#334155" penwidth=2 style="solid"] + build_matrix -> out_matrix [color="#16a34a" penwidth=2 style="solid"] + util_schema -> registry [color="#7c3aed" penwidth=1.5 style="dashed"] + util_sources_yaml -> registry [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_6.dot b/docs/pipeline-diagrams/public/dots/uk/stage_6.dot new file mode 100644 index 000000000..433d3e4d7 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_6.dot @@ -0,0 +1,30 @@ +digraph "Stage 6: Weight fitting (Torch optimizer)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_frs [label=<
Enhanced FRS (fully imputed)
From Stage 4
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_matrix [label=<
Target matrix
From Stage 5
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + uprate [label=<
uprate_dataset(2025)
Scale all variables forward
uprating_factors.csv growth rates
Variable-specific scaling
Similar to US SOI uprating
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + init_weights [label=<
Initialize log-weights
Country-aware initialization
log(hh_weight / areas_per_hh)
0.01 noise for symmetry breaking
England/Scotland/Wales/NI aware
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + torch_optim [label=<
Torch Adam optimizer
512 epochs (32 in testing)
Learning rate: 0.1
Weights: (areas, households) tensor
Dropout: 5% regularization
Different from US L0 approach
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + sre_loss [label=<
SRE loss function
Symmetric Relative Error
min((1+x)/(1+y)-1, (1+y)/(1+x)-1)^2
Combined: local + national loss
Optional validation dropout
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + const_calib [label=<
Constituency calibration
650 parliamentary constituencies
Age demographics by constituency
Income distribution targets
UC, housing, council tax
Save every 10 epochs
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + out_const_weights [label=<
parliamentary_constituency_weights.h5
(650 × ~20K households)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_calibrate [label=<
utils/calibrate.py
calibrate_local_areas()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_uprating [label=<
uprating_factors.csv
PE uprating factors table
Similar to US uprating.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_progress [label=<
ProcessingProgress
Rich UI epoch tracking
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_l0 [label=<
L0 regularization
US uses neural network sparsification
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_clone_weights [label=<
Clone-based weight matrix
US has 430 clones × 12K records = 5.16M columns
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_frs -> uprate [color="#334155" penwidth=2 style="solid"] + uprate -> init_weights [color="#334155" penwidth=2 style="solid"] + in_matrix -> sre_loss [color="#334155" penwidth=2 style="solid" label="targets" fontcolor="#334155"] + init_weights -> torch_optim [color="#334155" penwidth=2 style="solid"] + sre_loss -> torch_optim [color="#334155" penwidth=2 style="solid"] + torch_optim -> const_calib [color="#334155" penwidth=2 style="solid"] + const_calib -> out_const_weights [color="#16a34a" penwidth=2 style="solid"] + util_calibrate -> torch_optim [color="#7c3aed" penwidth=1.5 style="dashed"] + util_uprating -> uprate [color="#7c3aed" penwidth=1.5 style="dashed"] + util_progress -> torch_optim [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_7.dot b/docs/pipeline-diagrams/public/dots/uk/stage_7.dot new file mode 100644 index 000000000..af1f02600 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_7.dot @@ -0,0 +1,32 @@ +digraph "Stage 7: Local area calibration" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_const_weights [label=<
parliamentary_constituency_weights.h5
From Stage 6 (650 × ~20K)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_frs [label=<
Enhanced FRS (uprated to 2025)
From Stage 6
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_matrix [label=<
Local authority target matrix
From Stage 5 (LA variant)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + la_init [label=<
Initialize LA weights
Country-aware log-weight init
360 local authorities
England, Scotland, Wales, NI
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + la_calib [label=<
Local authority calibration
360 areas, Torch optimizer
Same SRE loss as constituency
LA-specific targets: income, UC
Housing tenure, council tax
Age demographics by LA
512 epochs (32 in testing)
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + la_targets [label=<
LA target sources
ONS, DWP, VOA admin data
local_age.py: age by LA
local_income.py: median income
local_uc.py: UC caseloads
local_la_extras.py: tenure, rent
voa_council_tax.py: CT bands
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + boundary [label=<
Boundary changes
mapping_matrix.py
Handles constituency boundary changes
Maps old → new constituencies
> fillcolor="#ccfbf1" color="#14b8a6" style="filled,rounded"] + out_la_weights [label=<
local_authority_weights.h5
(360 × ~20K households)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_updated_hh [label=<
Updated household_weight
Sum across all areas
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_calibrate [label=<
utils/calibrate.py
calibrate_local_areas()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_loss [label=<
utils/loss.py
Loss matrix construction helpers
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_modal [label=<
Modal cloud compute
US runs 8 parallel workers on Modal
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_h5_build [label=<
Per-area H5 file build
US builds 51 state + 435 CD + city H5 files
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_geo_derive [label=<
derive_geography_from_blocks()
US derives 15 geo vars from census blocks
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_spm_recalc [label=<
SPM threshold recalculation
US recalculates SPM by local median rents
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_frs -> la_init [color="#334155" penwidth=2 style="solid"] + la_init -> la_calib [color="#334155" penwidth=2 style="solid"] + in_matrix -> la_calib [color="#334155" penwidth=2 style="solid" label="LA targets" fontcolor="#334155"] + la_targets -> la_calib [color="#334155" penwidth=2 style="solid"] + in_const_weights -> boundary [color="#334155" penwidth=2 style="solid"] + boundary -> la_calib [color="#334155" penwidth=2 style="solid"] + la_calib -> out_la_weights [color="#16a34a" penwidth=2 style="solid"] + la_calib -> out_updated_hh [color="#16a34a" penwidth=2 style="solid"] + util_calibrate -> la_calib [color="#7c3aed" penwidth=1.5 style="dashed"] + util_loss -> la_calib [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/uk/stage_8.dot b/docs/pipeline-diagrams/public/dots/uk/stage_8.dot new file mode 100644 index 000000000..911b6e580 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/uk/stage_8.dot @@ -0,0 +1,46 @@ +digraph "Stage 8: Validation & output" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_calibrated [label=<
Enhanced FRS (calibrated, 2025)
From Stage 6-7
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_const_weights [label=<
parliamentary_constituency_weights.h5
650 constituency weights
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_la_weights [label=<
local_authority_weights.h5
360 LA weights
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + downrate [label=<
uprate_dataset(2023)
Scale variables back to base year
Inverse of Stage 6 uprating
Preserves calibrated weights
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + validate [label=<
dataset.validate()
Check for NaN/Inf
Key variable checks
Weight sums match population
Similar to US validation step
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + save_frs [label=<
Save base FRS
frs_2023_24.h5
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + save_enhanced [label=<
Save enhanced FRS
enhanced_frs_2023_24.h5
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + subsample_frs [label=<
subsample_dataset(1000)
Tiny base FRS
Proportional weight sampling
Rescale to preserve totals
Similar to US downsampling
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + subsample_enhanced [label=<
subsample_dataset(1000)
Tiny enhanced FRS
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + upload [label=<
Upload to HuggingFace
upload_completed_datasets.py
4 dataset files
2 weight files
Similar to US HuggingFace upload
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_frs [label=<
frs_2023_24.h5
Base FRS (~500MB)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_enhanced [label=<
enhanced_frs_2023_24.h5
Fully calibrated (~1GB+)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_tiny_frs [label=<
frs_2023_24_tiny.h5
1K households (~10MB)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_tiny_enhanced [label=<
enhanced_frs_2023_24_tiny.h5
1K households (~20MB)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_hf [label=<
HuggingFace Hub
PolicyEngine/policyengine-uk-data
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + util_subsample [label=<
utils/subsample.py
subsample_dataset()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_upload [label=<
utils/data_upload.py
HuggingFace upload helpers
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + absent_manifest [label=<
Manifest + SHA256 checksums
US generates manifest.json for integrity
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_modal_volume [label=<
Modal volume staging
US uses Modal persistent volumes for resumability
absent from uk
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_calibrated -> downrate [color="#334155" penwidth=2 style="solid"] + downrate -> validate [color="#334155" penwidth=2 style="solid"] + validate -> save_frs [color="#334155" penwidth=2 style="solid"] + validate -> save_enhanced [color="#334155" penwidth=2 style="solid"] + save_frs -> out_frs [color="#16a34a" penwidth=2 style="solid"] + save_enhanced -> out_enhanced [color="#16a34a" penwidth=2 style="solid"] + save_frs -> subsample_frs [color="#334155" penwidth=2 style="solid"] + save_enhanced -> subsample_enhanced [color="#334155" penwidth=2 style="solid"] + subsample_frs -> out_tiny_frs [color="#16a34a" penwidth=2 style="solid"] + subsample_enhanced -> out_tiny_enhanced [color="#16a34a" penwidth=2 style="solid"] + out_frs -> upload [color="#334155" penwidth=2 style="solid"] + out_enhanced -> upload [color="#334155" penwidth=2 style="solid"] + out_tiny_frs -> upload [color="#334155" penwidth=2 style="solid"] + out_tiny_enhanced -> upload [color="#334155" penwidth=2 style="solid"] + in_const_weights -> upload [color="#334155" penwidth=2 style="solid"] + in_la_weights -> upload [color="#334155" penwidth=2 style="solid"] + upload -> out_hf [color="#16a34a" penwidth=2 style="solid"] + util_subsample -> subsample_frs [color="#7c3aed" penwidth=1.5 style="dashed"] + util_subsample -> subsample_enhanced [color="#7c3aed" penwidth=1.5 style="dashed"] + util_upload -> upload [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/overview.dot b/docs/pipeline-diagrams/public/dots/us/overview.dot new file mode 100644 index 000000000..0ceec9bd7 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/overview.dot @@ -0,0 +1,38 @@ +digraph "Pipeline Overview — Cross-Stage Data Flow" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + stage0 [label=<
Stage 0: Raw Data Download
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage1 [label=<
Stage 1: Base Dataset Construction
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage2 [label=<
Stage 2: Extended CPS (PUF Clone)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage3 [label=<
Stage 3: Stratified CPS
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage4 [label=<
Stage 4: Source Imputation
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage5 [label=<
Stage 5: Matrix Build
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage6 [label=<
Stage 6: Weight Fitting (L0)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage7 [label=<
Stage 7: Local Area H5 Build
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + stage8 [label=<
Stage 8: Validation & Promotion
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + modal1 [label=<
Modal (CPU)
65GB RAM, 8 CPU
Stages 5-6 build phase
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + modal2 [label=<
Modal (GPU)
T4/A10/A100/H100
Stage 6 fit phase
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + modal3 [label=<
Modal (Workers)
8 containers, 16GB each
Stage 7 parallel build
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + hf1 [label=<
HuggingFace Hub
policyengine/policyengine-us-data
Download + Upload at every stage
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + local [label=<
Local Machine
make data / make pipeline
Stages 0-3 run locally
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + + stage0 -> stage1 [color="#334155" penwidth=2 style="solid" label="census_cps_2024.h5, irs_puf_2015.h5, census_acs_2022.h5" fontcolor="#334155"] + stage1 -> stage2 [color="#334155" penwidth=2 style="solid" label="CPS_2024, PUF_2024, ACS_2022" fontcolor="#334155"] + stage2 -> stage3 [color="#334155" penwidth=2 style="solid" label="extended_cps_2024.h5" fontcolor="#334155"] + stage3 -> stage4 [color="#334155" penwidth=2 style="solid" label="stratified_extended_cps_2024.h5" fontcolor="#334155"] + stage4 -> stage5 [color="#334155" penwidth=2 style="solid" label="source_imputed_stratified_extended_cps.h5" fontcolor="#334155"] + stage5 -> stage6 [color="#334155" penwidth=2 style="solid" label="calibration_package.pkl (X_sparse + targets)" fontcolor="#334155"] + stage6 -> stage7 [color="#334155" penwidth=2 style="solid" label="calibration_weights.npy, geography.npz" fontcolor="#334155"] + stage7 -> stage8 [color="#334155" penwidth=2 style="solid" label="51 state + 435 district + 1 city H5 files" fontcolor="#334155"] + modal1 -> stage5 [color="#dc2626" penwidth=1.5 style="dashed"] + modal2 -> stage6 [color="#dc2626" penwidth=1.5 style="dashed"] + modal3 -> stage7 [color="#dc2626" penwidth=1.5 style="dashed"] + hf1 -> stage0 [color="#b45309" penwidth=1.5 style="dotted" label="download artifacts" fontcolor="#b45309"] + stage8 -> hf1 [color="#b45309" penwidth=1.5 style="dotted" label="upload final datasets" fontcolor="#b45309"] + local -> stage0 [color="#9ca3af" penwidth=1 style="dotted" label="runs locally" fontcolor="#9ca3af"] + local -> stage1 [color="#9ca3af" penwidth=1 style="dotted"] + local -> stage2 [color="#9ca3af" penwidth=1 style="dotted"] + local -> stage3 [color="#9ca3af" penwidth=1 style="dotted"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_0.dot b/docs/pipeline-diagrams/public/dots/us/stage_0.dot new file mode 100644 index 000000000..ac88ab3bd --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_0.dot @@ -0,0 +1,43 @@ +digraph "Stage 0: Raw Data Download" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + cps_url [label=<
Census CPS ASEC
www2.census.gov
ZIP with CSV files
Person + Family + Household
Public HTTPS, no auth
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + acs_url [label=<
Census ACS PUMS
www2.census.gov
Person + Household CSVs
1-Year estimates
Public HTTPS
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + scf_url [label=<
Federal Reserve SCF
federalreserve.gov
Stata format (.dta)
Survey of Consumer Finances
Public HTTPS
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + hf_private [label=<
HuggingFace Private Repo
policyengine/irs-soi-puf
Requires HUGGING_FACE_TOKEN
IRS PUF + Demographics
SOI statistics
Population projections
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + hf_public [label=<
HuggingFace Public Repo
policyengine/policyengine-us-data
SIPP, block-CD distributions
calibration/policy_data.db
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + download_http [label=<
HTTP Download + ZIP Extract
requests.get() with streaming
Progress bar via tqdm
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + download_hf [label=<
HuggingFace Hub Download
huggingface_hub.hf_hub_download()
Token-authenticated for private repos
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + csv_parse [label=<
CSV/Stata Parsing
pandas read_csv / read_stata
Entity table construction
Tax unit grouping by TAX_ID
SPM unit grouping by SPM_ID
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_scf [label=<
SCF raw data
Stata -> DataFrame
Auto loans, net worth
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_sipp [label=<
pu2023_slim.csv
CSV (pipe-delimited)
SIPP microdata
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_block [label=<
block_cd_distributions.csv.gz
Compressed CSV
Census block populations
Block-to-CD crosswalk
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_pop [label=<
np2023_d5_mid.csv
CSV
Census population projections
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_cps [label=<
census_cps_2024.h5
HDF5 (pandas HDFStore)
5 entity tables
Person, Family, Household
Tax Unit, SPM Unit
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_acs [label=<
census_acs_2022.h5
HDF5 (pandas HDFStore)
Person + Household tables
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_puf [label=<
irs_puf_2015.h5
HDF5 (pandas HDFStore)
PUF + Demographics tables
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_soi [label=<
soi.csv
CSV
SOI aggregate statistics
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_calibration_db [label=<
policy_data.db
SQLite database
calibration/policy_data.db
Calibration targets
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + us_agencies [label=<
US Federal Data Sources
All inputs from US agencies
Census Bureau (CPS, ACS)
IRS (PUF, SOI)
Federal Reserve (SCF)
Census (SIPP, pop projections)
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + util_storage [label=<
STORAGE_FOLDER
policyengine_us_data/storage/
All downloads cached here
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + + cps_url -> download_http [color="#b45309" penwidth=1.5 style="dotted" label="CPS ASEC ZIP" fontcolor="#b45309"] + acs_url -> download_http [color="#b45309" penwidth=1.5 style="dotted" label="ACS PUMS CSV" fontcolor="#b45309"] + scf_url -> download_http [color="#b45309" penwidth=1.5 style="dotted" label="SCF .dta" fontcolor="#b45309"] + hf_private -> download_hf [color="#b45309" penwidth=1.5 style="dotted" label="PUF, demographics, SOI, pop" fontcolor="#b45309"] + hf_public -> download_hf [color="#b45309" penwidth=1.5 style="dotted" label="SIPP, block, policy_data.db" fontcolor="#b45309"] + download_http -> csv_parse [color="#334155" penwidth=2 style="solid" label="raw files" fontcolor="#334155"] + download_hf -> csv_parse [color="#334155" penwidth=2 style="solid" label="raw files" fontcolor="#334155"] + csv_parse -> out_cps [color="#16a34a" penwidth=2 style="solid" label="census_cps_2024.h5" fontcolor="#16a34a"] + csv_parse -> out_acs [color="#16a34a" penwidth=2 style="solid" label="census_acs_2022.h5" fontcolor="#16a34a"] + csv_parse -> out_puf [color="#16a34a" penwidth=2 style="solid" label="irs_puf_2015.h5" fontcolor="#16a34a"] + csv_parse -> out_soi [color="#16a34a" penwidth=2 style="solid" label="soi.csv" fontcolor="#16a34a"] + download_hf -> out_sipp [color="#16a34a" penwidth=2 style="solid" label="pu2023_slim.csv" fontcolor="#16a34a"] + download_hf -> out_block [color="#16a34a" penwidth=2 style="solid" label="block_cd_distributions.csv.gz" fontcolor="#16a34a"] + download_hf -> out_pop [color="#16a34a" penwidth=2 style="solid" label="np2023_d5_mid.csv" fontcolor="#16a34a"] + download_http -> out_scf [color="#16a34a" penwidth=2 style="solid" label="SCF raw data" fontcolor="#16a34a"] + download_hf -> out_calibration_db [color="#16a34a" penwidth=2 style="solid" label="policy_data.db" fontcolor="#16a34a"] + util_storage -> out_cps [color="#7c3aed" penwidth=1.5 style="dashed" label="caches to disk" fontcolor="#7c3aed"] +} diff --git a/docs/pipeline-diagrams/public/dots/us/stage_1.dot b/docs/pipeline-diagrams/public/dots/us/stage_1.dot new file mode 100644 index 000000000..29d9e3818 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_1.dot @@ -0,0 +1,73 @@ +digraph "Stage 1: Base Dataset Construction" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_census_cps [label=<
census_cps_2024.h5
From Stage 0
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_census_cps_prev [label=<
census_cps_2023.h5
Previous year for income matching
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_acs [label=<
ACS_2022
Training data for rent QRF
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_sipp [label=<
SIPP 2023
Training data for tips QRF
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_scf [label=<
SCF 2022
Training data for auto loans QRF
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + add_ids [label=<
add_id_variables()
person_id, household_id, tax_unit_id, spm_unit_id
Entity linkage keys
marital_unit_id from A_LINENO
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_personal [label=<
add_personal_variables()
age, sex, disability, occupation
80+ ages randomized to 80-84
12 overtime occupation flags
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_income [label=<
add_personal_income_variables()
30+ income types with splits
SS classified by reason codes
Retirement split by account type
Capital gains 88% LT / 12% ST
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_prev [label=<
add_previous_year_income()
Cross-year PERIDNUM linking
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_ssn [label=<
add_ssn_card_type()
US immigration classification
14 ASEC conditions
Undocumented target: 13M
SSN card types: 0-3
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + add_spm [label=<
add_spm_variables()
SPM thresholds + transfers
SNAP, housing, energy subsidies
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_hh [label=<
add_household_variables()
state_fips, county_fips
NYC flag from county FIPS
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_rent [label=<
add_rent()
QRF from ACS_2022
10K sampled household heads
Imputes rent + real_estate_taxes
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_tips [label=<
add_tips()
QRF from SIPP 2023
Tips + liquid assets
Models cached as pickle
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_auto [label=<
add_auto_loan()
QRF from SCF 2022
net_worth, auto_loan_balance
auto_loan_interest
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + add_takeup [label=<
add_takeup()
9 benefit programs
SNAP, ACA, Medicaid, EITC
SSI, TANF, WIC, Head Start
State-specific Medicaid rates
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + downsample [label=<
Downsampling
frac=0.5 for CPS_2024
Uses Microsimulation.subsample()
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_cps [label=<
cps_2024.h5
Dataset.ARRAYS format
~65K households (half-sample)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + in_puf_raw [label=<
irs_puf_2015.h5
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + puf_preprocess [label=<
preprocess_puf()
60+ IRS variable renames
E00200 -> employment_income
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + puf_qbi [label=<
QBI simulation
W-2 wages, UBIA, SSTB
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + puf_demo [label=<
impute_missing_demographics()
QRF for age, gender, earn split
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + puf_uprate [label=<
Uprating 2015->2021->2024
SOI growth rates by variable
Population-adjusted
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_puf [label=<
puf_2024.h5
Dataset.ARRAYS
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_rng [label=<
seeded_rng()
Deterministic per-variable RNG
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_retire [label=<
get_retirement_limits()
IRS contribution limits
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_spm [label=<
spm-calculator
SPM threshold computation
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_qrf [label=<
microimpute QRF
fit_predict() for sequential imputation
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_takeup [label=<
load_take_up_rate()
YAML parameter files
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_uprate [label=<
uprating.py
PE uprating factors table
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_registry [label=<
Unified imputation registry
Track which QRFs ran on what
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_lineage [label=<
Variable lineage tracker
Source provenance per variable
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + absent_benefits [label=<
UK benefit variables (20 types)
UK parses UC, PIP, DLA, JSA, ESA, etc.
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_brma [label=<
BRMA assignment
UK assigns Broad Rental Market Areas by region
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_council_tax [label=<
Council tax imputation
UK imputes council tax from FRS bands
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_census_cps -> add_ids [color="#334155" penwidth=2 style="solid" label="census_cps_2024.h5" fontcolor="#334155"] + in_census_cps_prev -> add_prev [color="#334155" penwidth=2 style="solid" label="census_cps_2023.h5" fontcolor="#334155"] + in_acs -> add_rent [color="#334155" penwidth=2 style="solid" label="ACS training data" fontcolor="#334155"] + in_sipp -> add_tips [color="#334155" penwidth=2 style="solid" label="SIPP training data" fontcolor="#334155"] + in_scf -> add_auto [color="#334155" penwidth=2 style="solid" label="SCF training data" fontcolor="#334155"] + add_ids -> add_personal [color="#334155" penwidth=2 style="solid"] + add_personal -> add_income [color="#334155" penwidth=2 style="solid"] + add_income -> add_prev [color="#334155" penwidth=2 style="solid"] + add_prev -> add_ssn [color="#334155" penwidth=2 style="solid"] + add_ssn -> add_spm [color="#334155" penwidth=2 style="solid"] + add_spm -> add_hh [color="#334155" penwidth=2 style="solid"] + add_hh -> add_rent [color="#334155" penwidth=2 style="solid"] + add_rent -> add_tips [color="#334155" penwidth=2 style="solid"] + add_tips -> add_auto [color="#334155" penwidth=2 style="solid"] + add_auto -> add_takeup [color="#334155" penwidth=2 style="solid"] + add_takeup -> downsample [color="#334155" penwidth=2 style="solid"] + downsample -> out_cps [color="#16a34a" penwidth=2 style="solid" label="cps_2024.h5" fontcolor="#16a34a"] + in_puf_raw -> puf_preprocess [color="#334155" penwidth=2 style="solid" label="irs_puf_2015.h5" fontcolor="#334155"] + puf_preprocess -> puf_qbi [color="#334155" penwidth=2 style="solid"] + puf_qbi -> puf_demo [color="#334155" penwidth=2 style="solid"] + puf_demo -> puf_uprate [color="#334155" penwidth=2 style="solid"] + puf_uprate -> out_puf [color="#16a34a" penwidth=2 style="solid" label="puf_2024.h5" fontcolor="#16a34a"] + util_rng -> add_income [color="#7c3aed" penwidth=1.5 style="dashed"] + util_retire -> add_income [color="#7c3aed" penwidth=1.5 style="dashed"] + util_spm -> add_spm [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> add_rent [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> add_tips [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> add_auto [color="#7c3aed" penwidth=1.5 style="dashed"] + util_takeup -> add_takeup [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> puf_demo [color="#7c3aed" penwidth=1.5 style="dashed"] + util_uprate -> puf_uprate [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_2.dot b/docs/pipeline-diagrams/public/dots/us/stage_2.dot new file mode 100644 index 000000000..4792333c1 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_2.dot @@ -0,0 +1,50 @@ +digraph "Stage 2: Extended CPS (PUF Clone + QRF Imputation)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_cps [label=<
CPS_2024_Full
From Stage 1 (full sample)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_puf [label=<
PUF_2024
From Stage 1
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_blocks [label=<
block_cd_distributions.csv.gz
Census block populations
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + geo_assign [label=<
Geography Assignment
assign_random_geography()
Population-weighted block draw
Derives state FIPS from block GEOID
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + record_double [label=<
Record Doubling
puf_clone_dataset()
CPS half: original values
PUF half: zero weight initially
IDs offset by max(original)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + qrf_pass1 [label=<
QRF Pass 1: Full Imputation
64 income variables
Training: PUF ~20K records
Predictors: 7 demographic vars
PUF half gets imputed values
CPS half keeps originals
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + qrf_pass2 [label=<
QRF Pass 2: Override Imputation
51 variables (both halves)
Partnership, S-corp income
Charitable, mortgage interest
Credits, deductions
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + retire_impute [label=<
Retirement Contribution Imputation
_impute_retirement_contributions()
traditional_401k, roth_401k
traditional_ira, roth_ira
self_employed_pension
IRS limits + SE pension cap applied
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + weeks_impute [label=<
Weeks Unemployed Imputation
_impute_weeks_unemployed()
QRF on CPS weeks_unemployed
Predictors: demographics + UC
Clips [0, 52]; zero if no UC
PUF half gets imputed values
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + ss_reconcile [label=<
SS Sub-component Reconciliation
reconcile_ss_subcomponents()
Retirement / Disability
Survivors / Dependents
Scaled to match PUF total
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + cps_only [label=<
CPS-Only Variable Re-imputation
80 variables for PUF half
Transfers: UC, TANF, SSI, WIC
SPM: total income, thresholds
Medical expenses, hours
Retirement distributions
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + formula_drop [label=<
Formula Variable Dropping
_drop_formula_variables()
Remove PE-computed variables
Rename: employment_income ->
employment_income_before_lsr
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_ext [label=<
extended_cps_2024.h5
Dataset.TIME_PERIOD_ARRAYS
~260K households (doubled)
CPS half + PUF half
Time-period dict structure
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + us_filing [label=<
IRS Filing Structure
Tax units, filing statuses
SINGLE, JOINT, HOH, SEPARATE
Dependent classification
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + util_puf_clone [label=<
puf_clone_dataset()
calibration/puf_impute.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_retire_constraints [label=<
get_retirement_limits()
utils/retirement_limits.py
IRS 401k/IRA limits + catch-up
SE pension rate + dollar cap
Year-specific from PE-US params
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_weeks_impute [label=<
_impute_weeks_unemployed()
calibration/puf_impute.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_reconcile [label=<
reconcile_ss_subcomponents()
calibration/puf_impute.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_qrf [label=<
microimpute QRF
fit_predict() for sequential imputation
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_quality [label=<
Imputation quality metrics
R-squared, coverage rates per variable
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_provenance [label=<
Variable provenance tracker
Record which donor survey each value came from
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_cps -> geo_assign [color="#334155" penwidth=2 style="solid" label="CPS records" fontcolor="#334155"] + in_blocks -> geo_assign [color="#334155" penwidth=2 style="solid" label="block populations" fontcolor="#334155"] + in_puf -> record_double [color="#334155" penwidth=2 style="solid" label="PUF records" fontcolor="#334155"] + in_cps -> record_double [color="#334155" penwidth=2 style="solid" label="CPS records" fontcolor="#334155"] + geo_assign -> record_double [color="#334155" penwidth=2 style="solid"] + record_double -> qrf_pass1 [color="#334155" penwidth=2 style="solid"] + qrf_pass1 -> qrf_pass2 [color="#334155" penwidth=2 style="solid"] + qrf_pass2 -> retire_impute [color="#334155" penwidth=2 style="solid"] + qrf_pass2 -> weeks_impute [color="#334155" penwidth=2 style="solid"] + retire_impute -> ss_reconcile [color="#334155" penwidth=2 style="solid"] + weeks_impute -> ss_reconcile [color="#334155" penwidth=2 style="solid"] + ss_reconcile -> cps_only [color="#334155" penwidth=2 style="solid"] + cps_only -> formula_drop [color="#334155" penwidth=2 style="solid"] + formula_drop -> out_ext [color="#16a34a" penwidth=2 style="solid"] + us_filing -> record_double [color="#9ca3af" penwidth=1 style="dotted"] + util_puf_clone -> record_double [color="#7c3aed" penwidth=1.5 style="dashed"] + util_retire_constraints -> retire_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_weeks_impute -> weeks_impute [color="#7c3aed" penwidth=1.5 style="dashed"] + util_reconcile -> ss_reconcile [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> qrf_pass1 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> qrf_pass2 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> cps_only [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_3.dot b/docs/pipeline-diagrams/public/dots/us/stage_3.dot new file mode 100644 index 000000000..2724cede3 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_3.dot @@ -0,0 +1,51 @@ +digraph "Stage 3: Stratified CPS + Enhanced CPS Reweighting" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_ext_cps [label=<
extended_cps_2024.h5
From Stage 2 (~260K HH)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + calc_agi [label=<
Calculate AGI
Microsimulation.calculate()
adjusted_gross_income mapped to household
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + strat_top [label=<
Retain Top 1% by AGI
All high-income households kept
Preserves tail representation
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + strat_sample [label=<
Uniform Sample Remaining 99%
Target: ~12,000 households
Optional 1.5x oversample of bottom 25%
Weights NOT adjusted
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_strat [label=<
stratified_extended_cps_2024.h5
~12K households
Input to Stages 4-6
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + in_ext_half [label=<
ExtendedCPS_2024_Half
Half-sample from Stage 2
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + build_loss [label=<
build_loss_matrix()
Hundreds of calibration targets
Returns (matrix, target_vector)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + t_soi [label=<
IRS SOI
AGI, income types, filer counts
By AGI band / filing status
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_census [label=<
Census Population
86 single-year age groups
np2023_d5_mid.csv
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_cbo [label=<
CBO Budget
Income tax, SNAP, SS, SSI, UC
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_state [label=<
State Targets
50 states + DC
Population, AGI, ACA, SNAP
Age groups, real estate taxes
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + t_hhs [label=<
HHS/CMS Medicaid
$900B spending, 72.4M enrolled
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_usda [label=<
USDA SNAP
State-level participation
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_treasury [label=<
Treasury EITC
Total + by child count
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + t_jct [label=<
JCT Tax Expenditures
SALT, medical, charitable
Counterfactual simulations
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + reweight [label=<
reweight()
PyTorch Adam + L0 HardConcrete
500 epochs, lr=0.2
L0 gates encourage sparsity
Minimize relative squared error
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + weight_validate [label=<
Weight Validation
No NaN, no negatives
100M < total HH < 200M
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_enhanced [label=<
enhanced_cps_2024.h5
Reweighted dataset
Production simulation dataset
Dataset.TIME_PERIOD_ARRAYS
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + util_strat [label=<
create_stratified_cps.py
calibration/
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_loss [label=<
build_loss_matrix()
utils/loss.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_l0 [label=<
HardConcrete L0
utils/l0.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_reweight [label=<
reweight()
PyTorch-based optimization
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_target_dash [label=<
Target validation dashboard
Visualize target fit quality
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_ext_cps -> calc_agi [color="#334155" penwidth=2 style="solid"] + calc_agi -> strat_top [color="#334155" penwidth=2 style="solid"] + strat_top -> strat_sample [color="#334155" penwidth=2 style="solid"] + strat_top -> out_strat [color="#334155" penwidth=2 style="solid" label="top 1%" fontcolor="#334155"] + strat_sample -> out_strat [color="#334155" penwidth=2 style="solid" label="sampled 99%" fontcolor="#334155"] + in_ext_half -> build_loss [color="#334155" penwidth=2 style="solid"] + t_soi -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_census -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_cbo -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_hhs -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_usda -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_treasury -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_jct -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + t_state -> build_loss [color="#b45309" penwidth=1.5 style="dotted"] + build_loss -> reweight [color="#334155" penwidth=2 style="solid" label="(matrix, targets)" fontcolor="#334155"] + reweight -> weight_validate [color="#334155" penwidth=2 style="solid"] + weight_validate -> out_enhanced [color="#16a34a" penwidth=2 style="solid"] + util_strat -> strat_sample [color="#7c3aed" penwidth=1.5 style="dashed"] + util_loss -> build_loss [color="#7c3aed" penwidth=1.5 style="dashed"] + util_l0 -> reweight [color="#7c3aed" penwidth=1.5 style="dashed"] + util_reweight -> reweight [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_4.dot b/docs/pipeline-diagrams/public/dots/us/stage_4.dot new file mode 100644 index 000000000..11f46c34a --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_4.dot @@ -0,0 +1,51 @@ +digraph "Stage 4: Source Imputation (ACS + SIPP + SCF)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_strat [label=<
stratified_extended_cps_2024.h5
From Stage 3 (~12K HH)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_acs [label=<
ACS_2022
American Community Survey
Has state_fips predictor
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_sipp_slim [label=<
SIPP 2023 (slim)
HuggingFace download
pu2023_slim.csv
No geographic identifiers
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_sipp_full [label=<
SIPP 2023 (full)
HuggingFace download
pu2023.csv (pipe-delimited)
Filtered to MONTHCODE=12
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_scf [label=<
SCF_2022
Survey of Consumer Finances
50% random subsample
No geographic identifiers
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + geo_assign [label=<
Geography Assignment
assign_random_geography(n_clones=1)
Census block from global distribution
State FIPS from block GEOID
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + acs_qrf [label=<
ACS QRF Imputation
10 predictors incl. state_fips
10K sampled ACS household heads
Imputes: rent, real_estate_taxes
Also creates pre_subsidy_rent
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + sipp_tips [label=<
SIPP Tips QRF
4 predictors, NO state
10K sampled records
Imputes: tip_income
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + sipp_assets [label=<
SIPP Assets QRF
5 predictors, NO state
20K sampled records
Imputes: bank_account_assets
stock_assets, bond_assets
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + scf_qrf [label=<
SCF QRF Imputation
8 predictors, NO state
Imputes: net_worth
auto_loan_balance
auto_loan_interest
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_imputed [label=<
source_imputed_stratified_extended_cps.h5
HDF5
Enriched with ACS/SIPP/SCF vars
Uploaded to HuggingFace
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_hf [label=<
HuggingFace Upload
calibration/ prefix
Available for Stage 5
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + util_clone_assign [label=<
clone_and_assign.py
Geography assignment
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_qrf [label=<
microimpute QRF
fit_predict() for sequential imputation
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_source_impute [label=<
source_impute.py
ACS/SIPP/SCF orchestrator
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_donor [label=<
Donor survey registry
Track which survey provides each variable
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_confidence [label=<
Imputation confidence scores
Uncertainty estimates per imputed value
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + us_sipp [label=<
SIPP Column Conventions
TXAMT=tips, TPTOTINC=income
US Census SIPP survey format
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + absent_consumption [label=<
LCFS consumption imputation
UK imputes 15 categories + NEED energy calibration
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_vat [label=<
VAT expenditure rate imputation
UK imputes from ETB survey
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_services [label=<
Public services imputation
UK imputes NHS, education, transport from ETB
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_cg [label=<
Capital gains optimization
UK uses Torch optimizer + Advani-Summers distribution
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_ss [label=<
Salary sacrifice imputation
UK 2-stage QRF + headcount targeting (5.4M)
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + absent_student_loan [label=<
Student loan plan assignment
UK assigns Plan 1/2/4/5 by age
absent from us
> fillcolor="#f3f4f6" color="#d1d5db" style="filled,rounded,dashed"] + + in_strat -> geo_assign [color="#334155" penwidth=2 style="solid"] + geo_assign -> acs_qrf [color="#334155" penwidth=2 style="solid" label="state_fips" fontcolor="#334155"] + in_acs -> acs_qrf [color="#334155" penwidth=2 style="solid"] + in_sipp_slim -> sipp_tips [color="#b45309" penwidth=1.5 style="dotted"] + in_sipp_full -> sipp_assets [color="#b45309" penwidth=1.5 style="dotted"] + in_scf -> scf_qrf [color="#b45309" penwidth=1.5 style="dotted"] + acs_qrf -> sipp_tips [color="#334155" penwidth=2 style="solid" label="chain" fontcolor="#334155"] + sipp_tips -> sipp_assets [color="#334155" penwidth=2 style="solid" label="chain" fontcolor="#334155"] + sipp_assets -> scf_qrf [color="#334155" penwidth=2 style="solid" label="chain" fontcolor="#334155"] + acs_qrf -> out_imputed [color="#16a34a" penwidth=2 style="solid"] + sipp_tips -> out_imputed [color="#16a34a" penwidth=2 style="solid"] + sipp_assets -> out_imputed [color="#16a34a" penwidth=2 style="solid"] + scf_qrf -> out_imputed [color="#16a34a" penwidth=2 style="solid"] + out_imputed -> out_hf [color="#16a34a" penwidth=2 style="solid"] + util_clone_assign -> geo_assign [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> acs_qrf [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> sipp_tips [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> sipp_assets [color="#7c3aed" penwidth=1.5 style="dashed"] + util_qrf -> scf_qrf [color="#7c3aed" penwidth=1.5 style="dashed"] + util_source_impute -> scf_qrf [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_5.dot b/docs/pipeline-diagrams/public/dots/us/stage_5.dot new file mode 100644 index 000000000..618be34a1 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_5.dot @@ -0,0 +1,62 @@ +digraph "Stage 5: Matrix Build (Calibration Target Construction)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_cps [label=<
source_imputed_stratified_extended_cps.h5
From Stage 4 (~12K HH)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_db [label=<
policy_data.db
SQLite calibration database
10-step ETL from 7 federal sources
strata + stratum_constraints + targets
target_overview SQL view
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_blocks [label=<
block_cd_distributions.csv.gz
Census block populations
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_aca [label=<
aca_ptc_multipliers_2022_2024.csv
State ACA PTC adjustments
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_config [label=<
target_config.yaml
Active target include list
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + target_resolve [label=<
Target Resolution
SQL query to target_overview
National + State + District targets
~8,000 rows total
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + target_uprate [label=<
Target Uprating
CPI-U for dollars, pop growth for counts
Matches data period to sim year 2024
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + geo_build [label=<
Geography Index Build
state_to_cols, cd_to_cols maps
Which columns belong to each geo
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + constraint_resolve [label=<
Constraint Resolution
Non-geographic constraints from DB
e.g., age >= 25 AND age < 35
medicaid_enrolled == True
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + state_precomp [label=<
Per-State Precomputation
51 fresh Microsimulations
One per state (50 + DC)
Clears cache between states
Computes ~50 vars per household
Most expensive step
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + county_precomp [label=<
Per-County Precomputation
ACA PTC only (~3,143 counties)
County affects PTC amount
Optional (county_level flag)
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + clone_assembly [label=<
Clone Assembly
430 clones x 12K records
= 5.16M columns total
Each clone = different geography
Avoids CD collisions across clones
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + takeup_rerand [label=<
Block-Level Takeup Re-randomization
Seeded on (block_geoid, hh_id)
SNAP, Medicaid, TANF, SSI, ACA
Ensures calibration consistency
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + sparse_build [label=<
Sparse Matrix Construction
COO triples -> CSR matrix
Shape: (n_targets, 5.16M)
Extremely sparse (~0.02% nonzero)
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + example_table [label=<
Example Matrix Rows (X)
RowTarget DescriptionSample Values [col 0..5]Target Value
0person_count, age 0-5, CD 101 (AK)[0, 0, 2, 0, 1, 0, ...]8,500
500SNAP $, state 06 (CA)[0, 0, $4800, 0, $2400, ...]$7.2B
7000AGI, national[$85K, $42K, $120K, ...]$15.1T
7500tax_unit_count, dividend_income > 0[0, 1, 0, 1, 0, ...]24.8M
> fillcolor="#ffffff" color="#94a3b8" style="filled,rounded"] + out_pkg [label=<
calibration_package.pkl
Python pickle
X_sparse (CSR matrix)
targets_df (DataFrame)
initial_weights, cd_geoid
block_geoid, metadata
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + etl_census [label=<
Census ACS 5-Year
Age distributions, SNAP
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + etl_irs [label=<
IRS SOI
Tax variables, EITC, AGI brackets
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + etl_usda [label=<
USDA FNS
SNAP participation by state
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + etl_cdc [label=<
CDC VSRR
Pregnancy prevalence
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + etl_cbo [label=<
CBO/CMS
Budget projections, Medicaid
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + etl_db [label=<
10-Step ETL Pipeline
make database
Creates policy_data.db
Hierarchical stratum model
US > State > CD
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + util_sql [label=<
sqlalchemy
Database queries
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_pool [label=<
ProcessPoolExecutor
Parallel state computation
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_takeup [label=<
compute_block_takeup_for_entities()
utils/takeup.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_scipy [label=<
scipy.sparse
CSR/COO matrix construction
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_dash [label=<
Target validation dashboard
Visual target fit exploration
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_sparsity [label=<
Matrix sparsity analyzer
Profile matrix structure
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_cps -> target_resolve [color="#334155" penwidth=2 style="solid"] + in_db -> target_resolve [color="#b45309" penwidth=1.5 style="dotted" label="SQL targets" fontcolor="#b45309"] + in_config -> target_resolve [color="#334155" penwidth=2 style="solid" label="include list" fontcolor="#334155"] + target_resolve -> target_uprate [color="#334155" penwidth=2 style="solid"] + target_uprate -> geo_build [color="#334155" penwidth=2 style="solid"] + geo_build -> constraint_resolve [color="#334155" penwidth=2 style="solid"] + in_db -> constraint_resolve [color="#b45309" penwidth=1.5 style="dotted" label="stratum_constraints" fontcolor="#b45309"] + constraint_resolve -> state_precomp [color="#334155" penwidth=2 style="solid"] + in_cps -> state_precomp [color="#334155" penwidth=2 style="solid" label="household data" fontcolor="#334155"] + state_precomp -> county_precomp [color="#334155" penwidth=2 style="solid"] + in_aca -> county_precomp [color="#334155" penwidth=2 style="solid" label="PTC multipliers" fontcolor="#334155"] + county_precomp -> clone_assembly [color="#334155" penwidth=2 style="solid"] + in_blocks -> clone_assembly [color="#334155" penwidth=2 style="solid" label="block populations" fontcolor="#334155"] + clone_assembly -> takeup_rerand [color="#334155" penwidth=2 style="solid"] + takeup_rerand -> sparse_build [color="#334155" penwidth=2 style="solid"] + sparse_build -> out_pkg [color="#16a34a" penwidth=2 style="solid"] + sparse_build -> example_table [color="#9ca3af" penwidth=1 style="dotted" label="matrix rows" fontcolor="#9ca3af"] + etl_census -> etl_db [color="#b45309" penwidth=1.5 style="dotted"] + etl_irs -> etl_db [color="#b45309" penwidth=1.5 style="dotted"] + etl_usda -> etl_db [color="#b45309" penwidth=1.5 style="dotted"] + etl_cdc -> etl_db [color="#b45309" penwidth=1.5 style="dotted"] + etl_cbo -> etl_db [color="#b45309" penwidth=1.5 style="dotted"] + etl_db -> in_db [color="#16a34a" penwidth=2 style="solid" label="produces" fontcolor="#16a34a"] + util_sql -> target_resolve [color="#7c3aed" penwidth=1.5 style="dashed"] + util_pool -> state_precomp [color="#7c3aed" penwidth=1.5 style="dashed"] + util_takeup -> takeup_rerand [color="#7c3aed" penwidth=1.5 style="dashed"] + util_scipy -> sparse_build [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_6.dot b/docs/pipeline-diagrams/public/dots/us/stage_6.dot new file mode 100644 index 000000000..278003d8e --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_6.dot @@ -0,0 +1,49 @@ +digraph "Stage 6: Weight Fitting (L0 Calibration)" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_pkg [label=<
calibration_package.pkl
From Stage 5
X_sparse matrix
targets_df
initial_weights
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_config [label=<
target_config.yaml
Include/exclude rules
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + modal_gpu [label=<
Modal GPU Container
T4 / A10 / A100 / H100
32GB RAM, 8 CPU
4-hour timeout
policyengine-us-data-fit-weights
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + init_weights [label=<
Compute Initial Weights
Population-proportional per CD
Sum person_count targets per CD
weight = CD_pop / n_active_cols
Gives reasonable starting point
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + create_model [label=<
Create SparseCalibrationWeights
l0-python model
n_features = 5.16M
init_keep_prob = 0.999
log_weight_jitter_sd = 0.05
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + gate_detail [label=<
HardConcrete Gate Parameters
ParameterValuePurpose
beta0.35Gate temperature (sharpness)
gamma-0.1Hard-sigmoid lower bound
zeta1.1Hard-sigmoid upper bound
lambda_l0 (local)1e-8Keep ~3-4M records
lambda_l0 (national)1e-4Keep ~50K records
lambda_l21e-12L2 regularization
learning_rate0.15Adam optimizer lr
epochs100Training iterations
> fillcolor="#ffffff" color="#94a3b8" style="filled,rounded"] + fit_model [label=<
model.fit()
Adam optimizer training loop
Loss = relative_squared_error
+ lambda_l0 * L0(w)
+ lambda_l2 * ||w||^2
Forward: y_pred = X @ (w * gate)
Backward: gradients on w + gate
Per-epoch diagnostic logging
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + extract [label=<
Extract Weights
model.get_weights(deterministic=True)
Gate thresholded (not sampled)
Produces exact zeros
Nonzero count = active records
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + preset_compare [label=<
Two Calibration Presets
Presetlambda_l0Active RecordsUse Case
local1e-8~3-4MPer-state/district H5 files
national1e-4~50KWeb app simulation
> fillcolor="#ffffff" color="#94a3b8" style="filled,rounded"] + out_weights [label=<
calibration_weights.npy
Shape: (n_records * n_clones,)
One weight per (hh, clone) pair
Most entries are zero (sparse)
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_geo [label=<
geography.npz
NumPy compressed archive
block_geoid, cd_geoid
county_fips, state_fips
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_diag [label=<
unified_diagnostics.csv
Per-target error analysis
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_log [label=<
calibration_log.csv
Per-epoch training log
Weight distribution buckets
Sparsity % per epoch
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_config [label=<
unified_run_config.json
Hyperparameters + checksums
Git provenance
Artifact SHA256 hashes
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_hf [label=<
HuggingFace Upload
calibration/ prefix
Available for Stage 7
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + util_l0 [label=<
l0-python
SparseCalibrationWeights
HardConcrete gates
Differentiable L0 norm
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_pytorch [label=<
PyTorch
Adam optimizer, autograd
CUDA GPU acceleration
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_modal [label=<
Modal
Remote GPU execution
Secrets: huggingface-token
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_tune [label=<
Hyperparameter tuning framework
Automated lambda/lr search
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_converge [label=<
Convergence monitor
Early stopping, loss plateaus
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_early_stop [label=<
Early stopping
Halt when loss stabilizes
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_pkg -> init_weights [color="#334155" penwidth=2 style="solid"] + in_config -> init_weights [color="#334155" penwidth=2 style="solid"] + init_weights -> create_model [color="#334155" penwidth=2 style="solid"] + create_model -> fit_model [color="#334155" penwidth=2 style="solid"] + fit_model -> extract [color="#334155" penwidth=2 style="solid"] + extract -> out_weights [color="#16a34a" penwidth=2 style="solid"] + gate_detail -> create_model [color="#9ca3af" penwidth=1 style="dotted" label="parameters" fontcolor="#9ca3af"] + preset_compare -> fit_model [color="#9ca3af" penwidth=1 style="dotted" label="preset config" fontcolor="#9ca3af"] + modal_gpu -> fit_model [color="#dc2626" penwidth=1.5 style="dashed" label="runs on" fontcolor="#dc2626"] + extract -> out_geo [color="#16a34a" penwidth=2 style="solid"] + fit_model -> out_diag [color="#16a34a" penwidth=2 style="solid"] + fit_model -> out_log [color="#16a34a" penwidth=2 style="solid"] + fit_model -> out_config [color="#16a34a" penwidth=2 style="solid"] + out_weights -> out_hf [color="#16a34a" penwidth=2 style="solid"] + out_geo -> out_hf [color="#16a34a" penwidth=2 style="solid"] + out_diag -> out_hf [color="#16a34a" penwidth=2 style="solid"] + out_log -> out_hf [color="#16a34a" penwidth=2 style="solid"] + out_config -> out_hf [color="#16a34a" penwidth=2 style="solid"] + util_l0 -> create_model [color="#7c3aed" penwidth=1.5 style="dashed"] + util_pytorch -> fit_model [color="#7c3aed" penwidth=1.5 style="dashed"] + util_modal -> modal_gpu [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_7.dot b/docs/pipeline-diagrams/public/dots/us/stage_7.dot new file mode 100644 index 000000000..5c7aa8006 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_7.dot @@ -0,0 +1,64 @@ +digraph "Stage 7: Local Area H5 Build" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_weights [label=<
calibration_weights.npy
From Stage 6
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_geo [label=<
geography.npz
Block/CD/state assignments
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_dataset [label=<
source_imputed_stratified_extended_cps.h5
From Stage 4
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_db [label=<
policy_data.db
CD list for partitioning
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_rents [label=<
national_and_district_rents_2023.csv
SPM geographic adjustment
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_run_config [label=<
unified_run_config.json
Checksums for validation
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + modal_coord [label=<
Modal Coordinator
coordinate_publish()
8GB RAM, 24hr timeout
Orchestrates 3 phases
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + download [label=<
Download Calibration Inputs
download_calibration_inputs()
From HuggingFace Hub
Validates checksums
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + partition [label=<
Partition Work
partition_work()
8 workers default
Weights: state=5, city=3, dist=1
Round-robin distribution
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + phase1 [label=<
Phase 1: States
51 state H5 files (50 + DC)
8 workers in parallel
AL.h5, AK.h5, ..., WY.h5
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + phase2 [label=<
Phase 2: Districts
~435 congressional district H5s
8 workers in parallel
NC-01.h5, CA-52.h5, ...
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + phase3 [label=<
Phase 3: Cities
NYC (5 counties, 13 CDs)
City probability filtering
P(target counties | CD)
> fillcolor="#fce7f3" color="#ec4899" style="filled,rounded"] + build_h5 [label=<
build_h5() — Core Logic
18-step H5 construction
1. Load base simulation
2. Reshape weight matrix
3. CD subset filtering
4. Identify active clones
5. Build entity membership maps
6. Build clone index arrays
7. Assign new entity IDs
8. Derive geography from blocks
9. Calculate entity weights
10. Clone variable arrays
11. Override IDs + weights + geo
12. Recalculate SPM thresholds
13. Apply calibration takeup
14. Write H5 file
15. Verify counts
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + worker [label=<
Modal Worker Container
build_areas_worker
16GB RAM, 4 CPU each
4-hour timeout
Subprocess isolation
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + geo_derive [label=<
derive_geography_from_blocks()
15 geographic variables
state, county, tract, CBSA
SLDU, SLDL, place, VTD
PUMA, ZCTA from block GEOID
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + spm_recalc [label=<
SPM Threshold Recalculation
calculate_spm_thresholds_vectorized()
Local median rents
Family composition
Tenure type
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + takeup_apply [label=<
Takeup Re-application
apply_block_takeup_to_arrays()
9 takeup variables
Block-level seeded draws
Consistent with calibration
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_states [label=<
states/*.h5
51 files
AL.h5 through WY.h5
Full simulation variables
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_districts [label=<
districts/*.h5
~435 files
NC-01.h5, CA-52.h5, ...
Per-CD subsets
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_cities [label=<
cities/*.h5
1 file (NYC)
NYC.h5
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_manifest [label=<
manifest.json
SHA256 checksums
Generated during validation
> fillcolor="#dcfce7" color="#22c55e" style="filled,rounded"] + out_volume [label=<
Modal Volume
local-area-staging
Persistent across containers
Enables resumability
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + util_build [label=<
publish_local_area.build_h5()
calibration/publish_local_area.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_geo [label=<
derive_geography_from_blocks()
calibration/block_assignment.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_spm [label=<
calculate_spm_thresholds_vectorized()
calibration/calibration_utils.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_takeup [label=<
apply_block_takeup_to_arrays()
utils/takeup.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_manifest [label=<
generate_manifest()
utils/manifest.py
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_incremental [label=<
Incremental rebuild
Only rebuild changed states
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_diff [label=<
Diff-based validation
Compare against previous build
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_weights -> download [color="#334155" penwidth=2 style="solid"] + in_geo -> download [color="#334155" penwidth=2 style="solid"] + in_dataset -> download [color="#334155" penwidth=2 style="solid"] + in_rents -> download [color="#334155" penwidth=2 style="solid"] + in_run_config -> download [color="#334155" penwidth=2 style="solid"] + in_db -> partition [color="#b45309" penwidth=1.5 style="dotted" label="CD list" fontcolor="#b45309"] + download -> partition [color="#334155" penwidth=2 style="solid"] + partition -> phase1 [color="#334155" penwidth=2 style="solid"] + phase1 -> phase2 [color="#334155" penwidth=2 style="solid"] + phase2 -> phase3 [color="#334155" penwidth=2 style="solid"] + phase1 -> build_h5 [color="#334155" penwidth=2 style="solid" label="calls" fontcolor="#334155"] + phase2 -> build_h5 [color="#334155" penwidth=2 style="solid" label="calls" fontcolor="#334155"] + phase3 -> build_h5 [color="#334155" penwidth=2 style="solid" label="calls" fontcolor="#334155"] + build_h5 -> geo_derive [color="#334155" penwidth=2 style="solid" label="step 8" fontcolor="#334155"] + geo_derive -> spm_recalc [color="#334155" penwidth=2 style="solid" label="step 12" fontcolor="#334155"] + spm_recalc -> takeup_apply [color="#334155" penwidth=2 style="solid" label="step 13" fontcolor="#334155"] + modal_coord -> worker [color="#dc2626" penwidth=1.5 style="dashed" label="orchestrates" fontcolor="#dc2626"] + worker -> build_h5 [color="#dc2626" penwidth=1.5 style="dashed" label="runs" fontcolor="#dc2626"] + phase1 -> out_states [color="#16a34a" penwidth=2 style="solid"] + phase2 -> out_districts [color="#16a34a" penwidth=2 style="solid"] + phase3 -> out_cities [color="#16a34a" penwidth=2 style="solid"] + build_h5 -> out_manifest [color="#16a34a" penwidth=2 style="solid"] + build_h5 -> out_volume [color="#16a34a" penwidth=2 style="solid"] + util_build -> build_h5 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_geo -> geo_derive [color="#7c3aed" penwidth=1.5 style="dashed"] + util_spm -> spm_recalc [color="#7c3aed" penwidth=1.5 style="dashed"] + util_takeup -> takeup_apply [color="#7c3aed" penwidth=1.5 style="dashed"] + util_manifest -> out_manifest [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/dots/us/stage_8.dot b/docs/pipeline-diagrams/public/dots/us/stage_8.dot new file mode 100644 index 000000000..5e21e45a7 --- /dev/null +++ b/docs/pipeline-diagrams/public/dots/us/stage_8.dot @@ -0,0 +1,54 @@ +digraph "Stage 8: Validation & Promotion" { + graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"] + node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"] + edge [fontname="Helvetica" fontsize=9] + + in_h5s [label=<
51 state + 435 district + 1 city H5s
From Stage 7 (Modal volume)
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + in_db [label=<
policy_data.db
Validation targets
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + in_run_config [label=<
unified_run_config.json
Expected checksums
> fillcolor="#dbeafe" color="#3b82f6" style="filled,rounded"] + v1 [label=<
Layer 1: Manifest Verification
SHA256 checksums per file
generate_manifest() + verify_manifest()
Catches corruption/incomplete writes
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v2 [label=<
Layer 2: Structural Sanity
sanity_checks.py
Weight non-negativity
Entity ID uniqueness
No NaN/Inf in monetary vars
Person-to-household mapping
Boolean takeup validation
Per-capita range checks
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v3 [label=<
Layer 3: Target-Based Validation
validate_staging.py
Full Microsimulation per area
Compare sim values to targets
Subprocess isolation for memory
Outputs CSV with per-target errors
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v4 [label=<
Layer 4: Smoke Test
check_staging_sums.py
Sum 20 key vars across 51 states
Compare to national references
GDP ~$29T, pop ~335M
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v5 [label=<
Layer 5: National H5 Validation
validate_national_h5.py
>30% deviation flagged
Hardcoded reference values
Also runs sanity checks
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v6 [label=<
Layer 6: Pre-Upload Validation
upload_completed_datasets.py
File size minimums (100MB/50MB)
H5 structure check
Microsim aggregate checks
emp_income > $5T, weights 100-200M
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + v7 [label=<
Layer 7: Package Validation
validate_package.py
Matrix achievability
Target ratio analysis
Zero-row detection
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + staging_upload [label=<
Upload to Staging
upload_to_staging_hf()
Batches of 50 files/commit
Exponential backoff retry
staging/{path} in HF repo
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + atomic_promote [label=<
Atomic Promotion
promote_staging_to_production_hf()
Single commit with CommitOperationCopy
staging/{path} -> {path}
All-or-nothing deployment
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + gcs_upload [label=<
GCS Parallel Upload
gs://policyengine-us-data/
Version metadata on each blob
google.auth.default() credentials
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + staging_cleanup [label=<
Staging Cleanup
cleanup_staging_hf()
CommitOperationDelete
Removes staging/ files
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + pipeline_mirror [label=<
Pipeline Artifact Mirror
mirror_to_pipeline()
manifest_only=True
SHA256 checksums only
Files too large to double-upload
> fillcolor="#ffedd5" color="#f97316" style="filled,rounded"] + out_hf_prod [label=<
HuggingFace Production
policyengine/policyengine-us-data
states/, districts/, cities/
Final published datasets
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + out_gcs [label=<
Google Cloud Storage
gs://policyengine-us-data/
CDN/backup storage
Version metadata
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + out_pipeline [label=<
Pipeline Artifact Repo
policyengine-us-data-pipeline
stage_7_local_area/manifest.json
Audit trail
> fillcolor="#fef9c3" color="#eab308" style="filled,rounded"] + util_manifest [label=<
manifest.py
generate_manifest(), verify_manifest()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_sanity [label=<
sanity_checks.py
run_sanity_checks()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_validate [label=<
validate_staging.py
Full target comparison
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_upload [label=<
data_upload.py
staging/promote/cleanup
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + util_pipeline [label=<
pipeline_artifacts.py
mirror_to_pipeline()
> fillcolor="#f3e8ff" color="#a855f7" style="filled,rounded"] + miss_regression [label=<
Automated regression tests
Compare to previous release
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + miss_rollback [label=<
Promotion rollback
Undo a bad promotion
> fillcolor="#fee2e2" color="#ef4444" style="filled,rounded,dashed"] + + in_h5s -> v1 [color="#334155" penwidth=2 style="solid"] + in_run_config -> v1 [color="#334155" penwidth=2 style="solid" label="checksums" fontcolor="#334155"] + in_db -> v3 [color="#b45309" penwidth=1.5 style="dotted" label="targets" fontcolor="#b45309"] + v1 -> v2 [color="#334155" penwidth=2 style="solid"] + v2 -> v3 [color="#334155" penwidth=2 style="solid"] + v3 -> v4 [color="#334155" penwidth=2 style="solid"] + v4 -> v5 [color="#334155" penwidth=2 style="solid"] + v5 -> v6 [color="#334155" penwidth=2 style="solid"] + v6 -> v7 [color="#334155" penwidth=2 style="solid"] + v7 -> staging_upload [color="#334155" penwidth=2 style="solid" label="all pass" fontcolor="#334155"] + staging_upload -> atomic_promote [color="#334155" penwidth=2 style="solid"] + atomic_promote -> gcs_upload [color="#334155" penwidth=2 style="solid"] + gcs_upload -> staging_cleanup [color="#334155" penwidth=2 style="solid"] + staging_cleanup -> pipeline_mirror [color="#334155" penwidth=2 style="solid"] + atomic_promote -> out_hf_prod [color="#16a34a" penwidth=2 style="solid"] + gcs_upload -> out_gcs [color="#16a34a" penwidth=2 style="solid"] + pipeline_mirror -> out_pipeline [color="#16a34a" penwidth=2 style="solid"] + util_manifest -> v1 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_sanity -> v2 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_validate -> v3 [color="#7c3aed" penwidth=1.5 style="dashed"] + util_upload -> staging_upload [color="#7c3aed" penwidth=1.5 style="dashed"] + util_pipeline -> pipeline_mirror [color="#7c3aed" penwidth=1.5 style="dashed"] +} \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/favicon.svg b/docs/pipeline-diagrams/public/favicon.svg new file mode 100644 index 000000000..6893eb132 --- /dev/null +++ b/docs/pipeline-diagrams/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/icons.svg b/docs/pipeline-diagrams/public/icons.svg new file mode 100644 index 000000000..e9522193d --- /dev/null +++ b/docs/pipeline-diagrams/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/pipeline-diagrams/scripts/convert-to-dot.mjs b/docs/pipeline-diagrams/scripts/convert-to-dot.mjs new file mode 100644 index 000000000..adad7394d --- /dev/null +++ b/docs/pipeline-diagrams/scripts/convert-to-dot.mjs @@ -0,0 +1,298 @@ +#!/usr/bin/env node +/** + * One-time conversion script: ReactFlow JSX stage files → Graphviz DOT files. + * + * Reads each stage JSX, regex-extracts the `nodes` and `edges` arrays, + * mocks the spread-operator edge styles, evals the arrays, and writes + * Graphviz DOT files to public/dots/{us,uk}/. + */ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, ".."); +const SRC = path.join(ROOT, "src", "stages"); + +// ─── Node type → DOT attributes ──────────────────────────────────── +const NODE_STYLES = { + input_data: { + fillcolor: "#dbeafe", + color: "#3b82f6", + style: "filled,rounded", + }, + output_data: { + fillcolor: "#dcfce7", + color: "#22c55e", + style: "filled,rounded", + }, + process: { + fillcolor: "#ffedd5", + color: "#f97316", + style: "filled,rounded", + }, + utility: { + fillcolor: "#f3e8ff", + color: "#a855f7", + style: "filled,rounded", + }, + external: { + fillcolor: "#fef9c3", + color: "#eab308", + style: "filled,rounded", + }, + us_specific: { + fillcolor: "#fce7f3", + color: "#ec4899", + style: "filled,rounded", + }, + uk_specific: { + fillcolor: "#ccfbf1", + color: "#14b8a6", + style: "filled,rounded", + }, + missing: { + fillcolor: "#fee2e2", + color: "#ef4444", + style: "filled,rounded,dashed", + }, + absent: { + fillcolor: "#f3f4f6", + color: "#d1d5db", + style: "filled,rounded,dashed", + }, + under_construction: { + fillcolor: "#fffbeb", + color: "#f59e0b", + style: "filled,rounded,dashed", + }, + data_table: { + fillcolor: "#ffffff", + color: "#94a3b8", + style: "filled,rounded", + }, +}; + +// ─── Edge type → DOT attributes ──────────────────────────────────── +const EDGE_STYLES = { + dataFlow: { color: "#334155", penwidth: 2, style: "solid" }, + producesArtifact: { color: "#16a34a", penwidth: 2, style: "solid" }, + usesUtility: { color: "#7c3aed", penwidth: 1.5, style: "dashed" }, + externalSource: { color: "#b45309", penwidth: 1.5, style: "dotted" }, + runsOn: { color: "#dc2626", penwidth: 1.5, style: "dashed" }, + informational: { color: "#9ca3af", penwidth: 1, style: "dotted" }, +}; + +// ─── HTML entity escaping ────────────────────────────────────────── +function esc(str) { + if (!str) return ""; + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} + +// ─── Extract a JS array from JSX source code ─────────────────────── +function extractArray(code, varName) { + const regex = new RegExp(`const\\s+${varName}\\s*=\\s*\\[`); + const match = regex.exec(code); + if (!match) return []; + + let depth = 0; + let start = match.index + match[0].length - 1; + let i = start; + for (; i < code.length; i++) { + if (code[i] === "[") depth++; + else if (code[i] === "]") { + depth--; + if (depth === 0) break; + } + } + + let arrayStr = code.slice(start, i + 1); + + // Replace spread operators with a _type property + arrayStr = arrayStr + .replace(/\.\.\.dataFlow/g, '_type: "dataFlow"') + .replace(/\.\.\.producesArtifact/g, '_type: "producesArtifact"') + .replace(/\.\.\.usesUtility/g, '_type: "usesUtility"') + .replace(/\.\.\.externalSource/g, '_type: "externalSource"') + .replace(/\.\.\.runsOn/g, '_type: "runsOn"') + .replace(/\.\.\.informational/g, '_type: "informational"'); + + // Remove ReactFlow-specific properties (style, animated, sourceHandle, targetHandle) + // that would cause eval issues (style has nested objects with stroke etc.) + arrayStr = arrayStr.replace( + /style:\s*\{[^}]*\}/g, + '_rfStyle: "removed"' + ); + + try { + return eval(arrayStr); + } catch (e) { + console.error(` Failed to parse ${varName}:`, e.message); + return []; + } +} + +// ─── Build HTML label for a node ──────────────────────────────────── +function buildLabel(node) { + const d = node.data || {}; + + // Data table nodes get a real HTML table + if (node.type === "data_table" && d.columns && d.rows) { + let html = `<`; + // Title row spanning all columns + html += ``; + // Header row + html += ``; + for (const col of d.columns) { + html += ``; + } + html += ``; + // Data rows + for (const row of d.rows) { + html += ``; + for (const cell of row) { + html += ``; + } + html += ``; + } + html += `
${esc(d.label)}
${esc(col)}
${esc(cell)}
>`; + return html; + } + + // Standard node label + let html = `<`; + html += ``; + if (d.subtitle) { + html += ``; + } + if (d.pr) { + html += ``; + } + if (d.absentFrom) { + html += ``; + } + if (d.details && d.details.length > 0) { + const detailText = d.details.map(esc).join("
"); + html += ``; + } + html += `
${esc(d.label || node.id)}
${esc(d.subtitle)}
${esc(d.pr)}
absent from ${esc(d.absentFrom)}
${detailText}
>`; + return html; +} + +// ─── Generate DOT content ─────────────────────────────────────────── +function generateDot(title, nodes, edges) { + const lines = []; + lines.push(`digraph "${esc(title)}" {`); + lines.push(` graph [rankdir=TB fontname="Helvetica" bgcolor="transparent" label="" pad="0.5"]`); + lines.push(` node [shape=box fontname="Helvetica" fontsize=11 penwidth=2 margin="0.15,0.1"]`); + lines.push(` edge [fontname="Helvetica" fontsize=9]`); + lines.push(``); + + // Nodes + for (const node of nodes) { + const s = NODE_STYLES[node.type] || NODE_STYLES.process; + const label = buildLabel(node); + const attrs = [ + `label=${label}`, + `fillcolor="${s.fillcolor}"`, + `color="${s.color}"`, + `style="${s.style}"`, + ]; + lines.push(` ${node.id} [${attrs.join(" ")}]`); + } + + lines.push(``); + + // Edges + for (const edge of edges) { + const eType = edge._type || "dataFlow"; + const s = EDGE_STYLES[eType] || EDGE_STYLES.dataFlow; + const attrs = [ + `color="${s.color}"`, + `penwidth=${s.penwidth}`, + `style="${s.style}"`, + ]; + if (edge.label) { + attrs.push(`label="${esc(edge.label)}"`); + attrs.push(`fontcolor="${s.color}"`); + } + lines.push(` ${edge.source} -> ${edge.target} [${attrs.join(" ")}]`); + } + + lines.push(`}`); + return lines.join("\n"); +} + +// ─── Extract title from JSX source ────────────────────────────────── +function extractTitle(code) { + const match = code.match(/title="([^"]+)"/); + return match ? match[1] : "Pipeline Diagram"; +} + +// ─── Process a single JSX file ────────────────────────────────────── +function processFile(jsxPath, dotPath) { + const code = fs.readFileSync(jsxPath, "utf-8"); + const title = extractTitle(code); + const nodes = extractArray(code, "nodes"); + const edges = extractArray(code, "edges"); + + console.log( + ` ${path.basename(jsxPath)} → ${path.basename(dotPath)} (${nodes.length} nodes, ${edges.length} edges)` + ); + + const dot = generateDot(title, nodes, edges); + fs.mkdirSync(path.dirname(dotPath), { recursive: true }); + fs.writeFileSync(dotPath, dot); +} + +// ─── Main ─────────────────────────────────────────────────────────── +console.log("Converting US stages..."); +const usFiles = [ + ["Overview.jsx", "overview.dot"], + ["Stage0.jsx", "stage_0.dot"], + ["Stage1.jsx", "stage_1.dot"], + ["Stage2.jsx", "stage_2.dot"], + ["Stage3.jsx", "stage_3.dot"], + ["Stage4.jsx", "stage_4.dot"], + ["Stage5.jsx", "stage_5.dot"], + ["Stage6.jsx", "stage_6.dot"], + ["Stage7.jsx", "stage_7.dot"], + ["Stage8.jsx", "stage_8.dot"], +]; + +for (const [src, dst] of usFiles) { + processFile( + path.join(SRC, src), + path.join(ROOT, "public", "dots", "us", dst) + ); +} + +console.log("\nConverting UK stages..."); +const ukFiles = [ + ["uk/Overview.jsx", "overview.dot"], + ["uk/Stage0.jsx", "stage_0.dot"], + ["uk/Stage1.jsx", "stage_1.dot"], + ["uk/Stage2.jsx", "stage_2.dot"], + ["uk/Stage3.jsx", "stage_3.dot"], + ["uk/Stage4.jsx", "stage_4.dot"], + ["uk/Stage5.jsx", "stage_5.dot"], + ["uk/Stage6.jsx", "stage_6.dot"], + ["uk/Stage7.jsx", "stage_7.dot"], + ["uk/Stage8.jsx", "stage_8.dot"], + ["uk/Stage9.jsx", "stage_9.dot"], + ["uk/Stage10.jsx", "stage_10.dot"], +]; + +for (const [src, dst] of ukFiles) { + processFile( + path.join(SRC, src), + path.join(ROOT, "public", "dots", "uk", dst) + ); +} + +console.log("\nDone! Generated DOT files in public/dots/"); diff --git a/docs/pipeline-diagrams/scripts/update-diagrams.mjs b/docs/pipeline-diagrams/scripts/update-diagrams.mjs new file mode 100644 index 000000000..963b340ab --- /dev/null +++ b/docs/pipeline-diagrams/scripts/update-diagrams.mjs @@ -0,0 +1,410 @@ +#!/usr/bin/env node + +/** + * Automated pipeline diagram updater. + * + * Three-phase pipeline run from CI (GitHub Actions) on PRs that change Python code: + * Phase 1 — Evaluate whether the stage list itself changed (conservative, biased toward NO) + * Phase 2 — For each affected stage, spawn a Claude Code CLI agent to update the DOT file + * Phase 3 — Validate all modified DOT files; if invalid, spawn a fix agent (up to 3 retries) + * + * Usage: + * ANTHROPIC_API_KEY=... node scripts/update-diagrams.mjs + */ + +import { execSync, spawn } from "child_process"; +import { readFileSync } from "fs"; +import { resolve, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = resolve(__dirname, ".."); // docs/pipeline-diagrams/ +const REPO_ROOT = resolve(ROOT, "../.."); // policyengine-us-data/ +const MANIFEST_PATH = resolve(ROOT, "manifest.json"); + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function loadManifest() { + return JSON.parse(readFileSync(MANIFEST_PATH, "utf-8")); +} + +/** Return the list of files changed in this push (or PR for local testing). */ +function getChangedFiles() { + const base = process.env.DIFF_BASE; + const head = process.env.DIFF_HEAD; + + // CI: use the before..after SHAs from the push event + if (base && head) { + const raw = execSync(`git diff --name-only ${base}..${head}`, { + cwd: REPO_ROOT, + encoding: "utf-8", + }); + return raw.trim().split("\n").filter(Boolean); + } + + // Local / fallback: diff against origin/main or HEAD~1 + try { + const raw = execSync("git diff --name-only origin/main...HEAD", { + cwd: REPO_ROOT, + encoding: "utf-8", + }); + return raw.trim().split("\n").filter(Boolean); + } catch { + console.warn("Could not compute diff vs origin/main — falling back to HEAD~1"); + const raw = execSync("git diff --name-only HEAD~1", { + cwd: REPO_ROOT, + encoding: "utf-8", + }); + return raw.trim().split("\n").filter(Boolean); + } +} + +/** Spawn `claude` CLI and return its stdout. */ +function runClaude(prompt, { maxTurns = 10, allowedTools = "Read,Grep,Glob" } = {}) { + return new Promise((resolve, reject) => { + const args = [ + "--print", + "--model", "sonnet", + "--max-turns", String(maxTurns), + "--allowedTools", allowedTools, + "--permission-mode", "bypassPermissions", + ]; + + const child = spawn("claude", args, { + cwd: REPO_ROOT, + env: { ...process.env }, + stdio: ["pipe", "pipe", "pipe"], + }); + + // Pipe prompt via stdin to avoid shell escaping issues with long prompts + child.stdin.write(prompt); + child.stdin.end(); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (d) => { stdout += d; }); + child.stderr.on("data", (d) => { stderr += d; }); + + child.on("close", (code) => { + if (code === 0) { + resolve(stdout); + } else { + reject(new Error(`claude exited with code ${code}: ${stderr}`)); + } + }); + + child.on("error", (err) => { + reject(new Error(`Failed to spawn claude: ${err.message}`)); + }); + }); +} + +/** Match a file path against a glob-like pattern (supports trailing *). */ +function matchesGlob(filePath, pattern) { + if (pattern.endsWith("/*")) { + return filePath.startsWith(pattern.slice(0, -1)); + } + if (pattern.endsWith("/*.py")) { + const dir = pattern.slice(0, -5); + return filePath.startsWith(dir + "/") && filePath.endsWith(".py") && !filePath.slice(dir.length + 1).includes("/"); + } + return filePath === pattern; +} + +// --------------------------------------------------------------------------- +// Phase 1: Stage List Evaluation +// --------------------------------------------------------------------------- + +async function phase1(manifest, changedFiles) { + console.log("\n=== Phase 1: Stage List Evaluation ==="); + + // Collect all mapped paths + const allMapped = new Set(); + for (const country of Object.values(manifest.countries)) { + for (const stage of country.stages) { + for (const glob of stage.sourceGlobs) { + allMapped.add(glob); + } + } + } + for (const file of Object.keys(manifest.crossStageUtilities)) { + allMapped.add(file); + } + + // Filter to Python files in relevant directories + const relevantDirs = ["policyengine_us_data/", "modal_app/"]; + const pythonChanges = changedFiles.filter( + (f) => f.endsWith(".py") && relevantDirs.some((d) => f.startsWith(d)) + ); + + if (pythonChanges.length === 0) { + console.log("No Python source changes detected. Skipping phase 1."); + return false; + } + + // Find orphan files (changed Python files not covered by any mapping) + const orphans = pythonChanges.filter((f) => { + for (const pattern of allMapped) { + if (matchesGlob(f, pattern)) return false; + } + return true; + }); + + if (orphans.length === 0) { + console.log("All changed files are mapped to existing stages. No stage list change needed."); + return false; + } + + // Only flag orphans in pipeline-critical directories + const pipelineOrphans = orphans.filter( + (f) => f.includes("/calibration/") || f.includes("/datasets/") || f.includes("/db/") + ); + + if (pipelineOrphans.length === 0) { + console.log(`${orphans.length} unmapped file(s) found, but none in pipeline-critical directories. Skipping.`); + return false; + } + + console.log(`${pipelineOrphans.length} unmapped pipeline file(s) found. Evaluating with AI...`); + + try { + const prompt = `The following Python files were changed in a PR but are NOT mapped to any pipeline stage in the manifest: + +${pipelineOrphans.map((f) => ` - ${f}`).join("\n")} + +The current manifest has these US stages: +${manifest.countries.us.stages.map((s) => ` Stage ${s.id}: ${s.title}`).join("\n")} + +Question: Does the pipeline stage list need to change? +DEFAULT ANSWER: NO. Only answer YES if a genuinely NEW pipeline stage was added (not just a new file within an existing stage). + +If NO: respond with exactly "NO_CHANGE" +If YES: respond with "STAGE_CHANGE" followed by a brief explanation of what changed.`; + + const result = await runClaude(prompt, { maxTurns: 6, allowedTools: "Read,Grep,Glob" }); + + if (result.includes("NO_CHANGE")) { + console.log("AI determined no stage list change is needed."); + return false; + } + + console.log("AI suggests stage list may need updating:"); + console.log(result.slice(0, 500)); + console.log("NOTE: Stage list changes require manual manifest.json update. Continuing with current stages."); + return false; // Don't auto-modify the stage list — flag for human review + } catch (err) { + console.warn(`Phase 1 AI call failed: ${err.message}. Continuing without stage list changes.`); + return false; + } +} + +// --------------------------------------------------------------------------- +// Phase 2: Per-Stage DOT Update +// --------------------------------------------------------------------------- + +async function phase2(manifest, changedFiles) { + console.log("\n=== Phase 2: Per-Stage DOT Update ==="); + + const pythonChanges = changedFiles.filter((f) => f.endsWith(".py") || f.endsWith(".yaml")); + + if (pythonChanges.length === 0) { + console.log("No Python/YAML changes. Skipping phase 2."); + return []; + } + + // Map changed files to affected stages + const affectedStages = []; + + for (const stage of manifest.countries.us.stages) { + const isAffected = pythonChanges.some((f) => + stage.sourceGlobs.some((g) => matchesGlob(f, g)) + ); + + // Also check cross-stage utilities + const utilAffected = pythonChanges.some((f) => { + const stageIds = manifest.crossStageUtilities[f]; + return stageIds && stageIds.includes(stage.id); + }); + + if (isAffected || utilAffected) { + affectedStages.push(stage); + } + } + + if (affectedStages.length === 0) { + console.log("No stages affected by the changed files."); + return []; + } + + // Cap at 4 stages per run for cost control + const stagesToProcess = affectedStages.slice(0, 4); + if (affectedStages.length > 4) { + console.warn(`${affectedStages.length} stages affected — processing first 4 only.`); + } + + console.log(`Affected stages: ${stagesToProcess.map((s) => `${s.id} (${s.desc})`).join(", ")}`); + + // Get relevant changed files per stage + const stageChangedFiles = (stage) => { + const direct = pythonChanges.filter((f) => + stage.sourceGlobs.some((g) => matchesGlob(f, g)) + ); + const utils = pythonChanges.filter((f) => { + const stageIds = manifest.crossStageUtilities[f]; + return stageIds && stageIds.includes(stage.id); + }); + return [...new Set([...direct, ...utils])]; + }; + + // Spawn parallel agents + const modifiedDots = []; + const results = await Promise.allSettled( + stagesToProcess.map(async (stage) => { + const files = stageChangedFiles(stage); + const dotPath = resolve(ROOT, "public", stage.dotFile); + const dotRelative = `docs/pipeline-diagrams/public/${stage.dotFile}`; + + console.log(` Stage ${stage.id}: spawning agent for ${files.length} file(s)...`); + + const prompt = `You are updating a Graphviz DOT pipeline diagram for "${stage.title}". + +The following Python files were changed in this PR and affect this stage: +${files.map((f) => ` - ${f}`).join("\n")} + +The current DOT diagram is at: ${dotRelative} + +Your task: +1. Read the current DOT file at ${dotRelative} +2. Read the changed source files to understand what changed +3. Explore imports/dependencies if needed to understand the full picture +4. Update the DOT file to reflect the code changes +5. Follow the DOT style conventions in CLAUDE.md (node colors, edge types, HTML labels) +6. Do NOT remove nodes/edges unless functionality was clearly removed +7. Write the updated DOT file to the same path + +If the changes don't affect the diagram structure (e.g., only internal logic changes, no new inputs/outputs/steps), leave the DOT file unchanged and explain why.`; + + const result = await runClaude(prompt, { + maxTurns: 10, + allowedTools: "Read,Grep,Glob,Write", + }); + + // Check if the DOT was actually modified + try { + execSync(`git diff --quiet "${dotRelative}"`, { cwd: REPO_ROOT }); + } catch { + // git diff --quiet exits non-zero when file changed + modifiedDots.push(dotPath); + console.log(` Stage ${stage.id}: DOT file updated.`); + } + + return { stage: stage.id, result: "ok" }; + }) + ); + + // Log results + for (const r of results) { + if (r.status === "rejected") { + console.warn(` Agent failed: ${r.reason.message}`); + } + } + + console.log(`Phase 2 complete. ${modifiedDots.length} DOT file(s) modified.`); + return modifiedDots; +} + +// --------------------------------------------------------------------------- +// Phase 3: Structural Validation +// --------------------------------------------------------------------------- + +async function phase3(modifiedDots) { + console.log("\n=== Phase 3: Structural Validation ==="); + + if (modifiedDots.length === 0) { + console.log("No DOT files to validate."); + return; + } + + const { Graphviz } = await import("@hpcc-js/wasm-graphviz"); + const gv = await Graphviz.load(); + + for (const dotPath of modifiedDots) { + let content = readFileSync(dotPath, "utf-8"); + let valid = false; + + for (let attempt = 0; attempt < 3; attempt++) { + try { + gv.dot(content); + console.log(` VALID: ${dotPath}`); + valid = true; + break; + } catch (err) { + console.warn(` INVALID (attempt ${attempt + 1}): ${dotPath}: ${err.message}`); + + if (attempt < 2) { + try { + const dotRelative = dotPath.replace(REPO_ROOT + "/", ""); + const prompt = `The Graphviz DOT file at ${dotRelative} has a syntax error: + +${err.message} + +Read the file, fix the DOT syntax error while preserving the intended diagram structure, and write the corrected file back.`; + + await runClaude(prompt, { + maxTurns: 4, + allowedTools: "Read,Write", + }); + + content = readFileSync(dotPath, "utf-8"); + } catch (fixErr) { + console.warn(` Fix agent failed: ${fixErr.message}`); + } + } + } + } + + if (!valid) { + console.warn(` Reverting ${dotPath} to HEAD version after 3 failed fix attempts.`); + try { + const dotRelative = dotPath.replace(REPO_ROOT + "/", ""); + execSync(`git checkout HEAD -- "${dotRelative}"`, { cwd: REPO_ROOT }); + } catch { + console.error(` Could not revert ${dotPath}.`); + } + } + } + + console.log("Phase 3 complete."); +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +async function main() { + console.log("Pipeline Diagram Updater"); + console.log("========================"); + + const manifest = loadManifest(); + const changedFiles = getChangedFiles(); + + console.log(`Changed files: ${changedFiles.length}`); + + // Phase 1: Stage list evaluation + await phase1(manifest, changedFiles); + + // Phase 2: Per-stage DOT update + const modifiedDots = await phase2(manifest, changedFiles); + + // Phase 3: Structural validation + await phase3(modifiedDots); + + console.log("\nDone."); +} + +main().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); diff --git a/pipeline_stages.yaml b/pipeline_stages.yaml new file mode 100644 index 000000000..2a1ca3d00 --- /dev/null +++ b/pipeline_stages.yaml @@ -0,0 +1,883 @@ +# Pipeline stage definitions and cross-node edges. +# Nodes are defined via @pipeline_node decorators in source code. +# This file defines: stage groupings, input/output/utility nodes, and edges. +# The extraction script merges decorated functions with these definitions. + +stages: + # ══════════════════════════════════════════════════════════════ + # Stage 0: Raw Data Download + # ══════════════════════════════════════════════════════════════ + - id: 0 + label: "Stage 0" + title: "Stage 0: Raw Data Download" + description: "Download raw survey data from Census, IRS, Federal Reserve, and HuggingFace" + country: us + extra_nodes: + - id: cps_url + label: "Census CPS ASEC" + node_type: input + description: "ZIP with CSV files from www2.census.gov" + - id: acs_url + label: "Census ACS PUMS" + node_type: input + description: "Person + Household CSVs, 1-Year estimates" + - id: scf_url + label: "Federal Reserve SCF" + node_type: input + description: "Stata format (.dta), Survey of Consumer Finances" + - id: hf_private + label: "HuggingFace Private Repo" + node_type: external + description: "policyengine/irs-soi-puf — requires HUGGING_FACE_TOKEN" + - id: hf_public + label: "HuggingFace Public Repo" + node_type: external + description: "policyengine/policyengine-us-data — SIPP, block distributions, policy_data.db" + - id: download_http + label: "HTTP Download + ZIP Extract" + node_type: process + description: "requests.get() with streaming, tqdm progress bar" + - id: download_hf + label: "HuggingFace Hub Download" + node_type: process + description: "hf_hub_download(), token-authenticated for private repos" + - id: csv_parse + label: "CSV/Stata Parsing" + node_type: process + description: "pandas read_csv/read_stata, entity table construction" + - id: out_cps_raw + label: "census_cps_2024.h5" + node_type: output + description: "5 entity tables: Person, Family, Household, Tax Unit, SPM Unit" + - id: out_acs_raw + label: "census_acs_2022.h5" + node_type: output + description: "Person + Household tables" + - id: out_puf_raw + label: "irs_puf_2015.h5" + node_type: output + description: "PUF + Demographics tables" + - id: out_soi + label: "soi.csv" + node_type: output + description: "SOI aggregate statistics" + - id: out_scf + label: "SCF raw data" + node_type: output + description: "Stata → DataFrame, auto loans + net worth" + - id: out_sipp + label: "pu2023_slim.csv" + node_type: output + description: "SIPP microdata (pipe-delimited)" + - id: out_block + label: "block_cd_distributions.csv.gz" + node_type: output + description: "Census block populations, block-to-CD crosswalk" + - id: out_pop + label: "np2023_d5_mid.csv" + node_type: output + description: "Census population projections" + - id: out_calibration_db + label: "policy_data.db" + node_type: output + description: "SQLite calibration targets database" + - id: util_storage + label: "STORAGE_FOLDER" + node_type: utility + description: "policyengine_us_data/storage/ — all downloads cached here" + edges: + - source: cps_url + target: download_http + edge_type: external_source + label: "CPS ASEC ZIP" + - source: acs_url + target: download_http + edge_type: external_source + label: "ACS PUMS CSV" + - source: scf_url + target: download_http + edge_type: external_source + label: "SCF .dta" + - source: hf_private + target: download_hf + edge_type: external_source + label: "PUF, demographics, SOI, pop" + - source: hf_public + target: download_hf + edge_type: external_source + label: "SIPP, block, policy_data.db" + - source: download_http + target: csv_parse + edge_type: data_flow + label: "raw files" + - source: download_hf + target: csv_parse + edge_type: data_flow + label: "raw files" + - source: csv_parse + target: out_cps_raw + edge_type: produces_artifact + label: "census_cps_2024.h5" + - source: csv_parse + target: out_acs_raw + edge_type: produces_artifact + label: "census_acs_2022.h5" + - source: csv_parse + target: out_puf_raw + edge_type: produces_artifact + label: "irs_puf_2015.h5" + - source: csv_parse + target: out_soi + edge_type: produces_artifact + label: "soi.csv" + - source: download_http + target: out_scf + edge_type: produces_artifact + label: "SCF raw data" + - source: download_hf + target: out_sipp + edge_type: produces_artifact + label: "pu2023_slim.csv" + - source: download_hf + target: out_block + edge_type: produces_artifact + label: "block_cd_distributions.csv.gz" + - source: download_hf + target: out_pop + edge_type: produces_artifact + label: "np2023_d5_mid.csv" + - source: download_hf + target: out_calibration_db + edge_type: produces_artifact + label: "policy_data.db" + + # ══════════════════════════════════════════════════════════════ + # Stage 1: Base Dataset Construction + # ══════════════════════════════════════════════════════════════ + - id: 1 + label: "Stage 1" + title: "Stage 1: Base Dataset Construction" + description: "Build CPS 2024 and PUF 2024 from raw survey data" + country: us + extra_nodes: + - id: in_census_cps + label: "census_cps_2024.h5" + node_type: input + description: "Raw CPS ASEC from Stage 0" + - id: in_census_cps_prev + label: "census_cps_2023.h5" + node_type: input + description: "Previous year CPS for income matching" + - id: in_acs + label: "ACS 2022" + node_type: input + description: "Training data for rent QRF" + - id: in_sipp + label: "SIPP 2023" + node_type: input + description: "Training data for tips QRF" + - id: in_scf + label: "SCF 2022" + node_type: input + description: "Training data for auto loans QRF" + - id: in_uprating + label: "uprating_factors.csv" + node_type: input + description: "PE uprating factors table" + - id: out_cps + label: "cps_2024.h5" + node_type: output + description: "Dataset.ARRAYS format, ~65K households (half-sample)" + - id: out_puf + label: "puf_2024.h5" + node_type: output + description: "Dataset.ARRAYS format" + - id: in_irs_puf + label: "irs_puf_2015.h5" + node_type: input + description: "Raw IRS PUF from Stage 0" + - id: in_demographics + label: "demographics_2015.csv" + node_type: input + description: "PUF demographics from HuggingFace" + - id: util_seeded_rng + label: "seeded_rng()" + node_type: utility + description: "Deterministic per-variable RNG" + - id: util_qrf + label: "microimpute QRF" + node_type: utility + description: "fit_predict() for sequential imputation" + - id: util_retirement_limits + label: "get_retirement_limits()" + node_type: utility + description: "IRS contribution limits" + edges: + # CPS processing chain + - { source: in_census_cps, target: add_id_variables, edge_type: data_flow, label: "raw CPS tables" } + - { source: add_id_variables, target: add_personal_variables, edge_type: data_flow } + - { source: add_personal_variables, target: add_personal_income_variables, edge_type: data_flow } + - { source: add_personal_income_variables, target: add_previous_year_income, edge_type: data_flow } + - { source: in_census_cps_prev, target: add_previous_year_income, edge_type: data_flow, label: "prior year PERIDNUM" } + - { source: add_previous_year_income, target: add_ssn_card_type, edge_type: data_flow } + - { source: add_ssn_card_type, target: add_spm_variables, edge_type: data_flow } + - { source: add_spm_variables, target: add_household_variables, edge_type: data_flow } + - { source: add_household_variables, target: add_rent, edge_type: data_flow } + - { source: in_acs, target: add_rent, edge_type: external_source, label: "ACS training data" } + - { source: add_rent, target: add_tips, edge_type: data_flow } + - { source: in_sipp, target: add_tips, edge_type: external_source, label: "SIPP training data" } + - { source: add_tips, target: add_auto_loan, edge_type: data_flow } + - { source: in_scf, target: add_auto_loan, edge_type: external_source, label: "SCF training data" } + - { source: add_auto_loan, target: add_takeup, edge_type: data_flow } + - { source: add_takeup, target: downsample, edge_type: data_flow } + - { source: downsample, target: out_cps, edge_type: produces_artifact, label: "cps_2024.h5" } + # PUF processing chain + - { source: in_irs_puf, target: preprocess_puf, edge_type: data_flow, label: "raw PUF records" } + - { source: preprocess_puf, target: simulate_qbi, edge_type: data_flow } + - { source: simulate_qbi, target: impute_puf_demographics, edge_type: data_flow } + - { source: in_demographics, target: impute_puf_demographics, edge_type: data_flow, label: "demographics_2015.csv" } + - { source: impute_puf_demographics, target: out_puf, edge_type: produces_artifact, label: "puf_2024.h5" } + - { source: in_uprating, target: out_puf, edge_type: data_flow, label: "SOI growth rates" } + # Utility edges + - { source: util_seeded_rng, target: add_takeup, edge_type: uses_utility } + - { source: util_qrf, target: add_rent, edge_type: uses_utility } + - { source: util_qrf, target: add_tips, edge_type: uses_utility } + - { source: util_qrf, target: add_auto_loan, edge_type: uses_utility } + - { source: util_retirement_limits, target: add_personal_income_variables, edge_type: uses_utility } + - { source: util_qrf, target: impute_puf_demographics, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 2: Extended CPS (PUF Clone + QRF Imputation) + # ══════════════════════════════════════════════════════════════ + - id: 2 + label: "Stage 2" + title: "Stage 2: Extended CPS (PUF Clone)" + description: "Merge CPS + PUF via cloning, impute 64 income vars + 51 override vars via QRF" + country: us + extra_nodes: + - id: in_cps_s2 + label: "CPS_2024_Full" + node_type: input + description: "From Stage 1 (full sample)" + - id: in_puf_s2 + label: "PUF_2024" + node_type: input + description: "From Stage 1" + - id: in_blocks_s2 + label: "block_cd_distributions.csv.gz" + node_type: input + description: "Census block populations" + - id: geo_assign_s2 + label: "Geography Assignment" + node_type: process + description: "assign_random_geography() — population-weighted block draw" + - id: record_double + label: "Record Doubling" + node_type: process + description: "puf_clone_dataset() — CPS half keeps originals, PUF half starts with zero weight" + - id: qrf_pass1 + label: "QRF Pass 1: Full Imputation" + node_type: process + description: "64 income variables — training on PUF ~20K records, 7 demographic predictors" + - id: qrf_pass2 + label: "QRF Pass 2: Override Imputation" + node_type: process + description: "51 variables (both halves) — partnership, S-corp, charitable, mortgage, credits" + - id: retire_impute + label: "Retirement Contribution Imputation" + node_type: process + description: "401k, IRA, SE pension — IRS limits + catch-up applied" + - id: weeks_impute + label: "Weeks Unemployed Imputation" + node_type: process + description: "QRF on CPS weeks_unemployed — clips [0, 52], zero if no UC" + - id: ss_reconcile + label: "SS Sub-component Reconciliation" + node_type: process + description: "Retirement/Disability/Survivors/Dependents — scaled to match PUF total" + - id: cps_only + label: "CPS-Only Variable Re-imputation" + node_type: process + description: "80 variables for PUF half — transfers, SPM, medical, hours, retirement" + - id: formula_drop + label: "Formula Variable Dropping" + node_type: process + description: "Remove PE-computed variables, rename employment_income → employment_income_before_lsr" + - id: out_ext + label: "extended_cps_2024.h5" + node_type: output + description: "~260K households (doubled), CPS half + PUF half" + - id: util_puf_clone + label: "puf_clone_dataset()" + node_type: utility + description: "calibration/puf_impute.py" + - id: util_qrf_s2 + label: "microimpute QRF" + node_type: utility + description: "fit_predict() for sequential imputation" + edges: + - { source: in_cps_s2, target: geo_assign_s2, edge_type: data_flow, label: "CPS records" } + - { source: in_blocks_s2, target: geo_assign_s2, edge_type: data_flow, label: "block populations" } + - { source: in_puf_s2, target: record_double, edge_type: data_flow, label: "PUF records" } + - { source: in_cps_s2, target: record_double, edge_type: data_flow, label: "CPS records" } + - { source: geo_assign_s2, target: record_double, edge_type: data_flow } + - { source: record_double, target: qrf_pass1, edge_type: data_flow } + - { source: qrf_pass1, target: qrf_pass2, edge_type: data_flow } + - { source: qrf_pass2, target: retire_impute, edge_type: data_flow } + - { source: qrf_pass2, target: weeks_impute, edge_type: data_flow } + - { source: retire_impute, target: ss_reconcile, edge_type: data_flow } + - { source: weeks_impute, target: ss_reconcile, edge_type: data_flow } + - { source: ss_reconcile, target: cps_only, edge_type: data_flow } + - { source: cps_only, target: formula_drop, edge_type: data_flow } + - { source: formula_drop, target: out_ext, edge_type: produces_artifact } + - { source: util_puf_clone, target: record_double, edge_type: uses_utility } + - { source: util_qrf_s2, target: qrf_pass1, edge_type: uses_utility } + - { source: util_qrf_s2, target: qrf_pass2, edge_type: uses_utility } + - { source: util_qrf_s2, target: cps_only, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 3: Stratified CPS + Enhanced CPS Reweighting + # ══════════════════════════════════════════════════════════════ + - id: 3 + label: "Stage 3" + title: "Stage 3: Stratified CPS + Enhanced CPS Reweighting" + description: "Stratify by income (keep top 1%), reweight to match calibration targets" + country: us + extra_nodes: + - id: in_ext_cps + label: "extended_cps_2024.h5" + node_type: input + description: "From Stage 2 (~260K HH)" + - id: calc_agi + label: "Calculate AGI" + node_type: process + description: "Microsimulation.calculate() — adjusted_gross_income mapped to household" + - id: strat_top + label: "Retain Top 1% by AGI" + node_type: process + description: "All high-income households kept — preserves tail representation" + - id: strat_sample + label: "Uniform Sample Remaining 99%" + node_type: process + description: "Target: ~12,000 households — optional 1.5x oversample of bottom 25%" + - id: out_strat + label: "stratified_extended_cps_2024.h5" + node_type: output + description: "~12K households — input to Stages 4-6" + - id: in_ext_half + label: "ExtendedCPS_2024_Half" + node_type: input + description: "Half-sample from Stage 2" + - id: build_loss + label: "build_loss_matrix()" + node_type: process + description: "Hundreds of calibration targets — returns (matrix, target_vector)" + - id: t_soi + label: "IRS SOI" + node_type: external + description: "AGI, income types, filer counts by AGI band / filing status" + - id: t_census + label: "Census Population" + node_type: external + description: "86 single-year age groups from np2023_d5_mid.csv" + - id: t_cbo + label: "CBO Budget" + node_type: external + description: "Income tax, SNAP, SS, SSI, UC" + - id: t_state + label: "State Targets" + node_type: us_specific + description: "50 states + DC — population, AGI, ACA, SNAP, age groups" + - id: t_jct + label: "JCT Tax Expenditures" + node_type: external + description: "SALT, medical, charitable — counterfactual simulations" + - id: reweight + label: "reweight()" + node_type: process + description: "PyTorch Adam + L0 HardConcrete — 500 epochs, lr=0.2" + - id: weight_validate + label: "Weight Validation" + node_type: process + description: "No NaN, no negatives — 100M < total HH < 200M" + - id: out_enhanced + label: "enhanced_cps_2024.h5" + node_type: output + description: "Reweighted production simulation dataset" + - id: util_loss + label: "build_loss_matrix()" + node_type: utility + description: "utils/loss.py" + - id: util_l0_s3 + label: "HardConcrete L0" + node_type: utility + description: "utils/l0.py" + edges: + - { source: in_ext_cps, target: calc_agi, edge_type: data_flow } + - { source: calc_agi, target: strat_top, edge_type: data_flow } + - { source: strat_top, target: strat_sample, edge_type: data_flow } + - { source: strat_top, target: out_strat, edge_type: data_flow, label: "top 1%" } + - { source: strat_sample, target: out_strat, edge_type: data_flow, label: "sampled 99%" } + - { source: in_ext_half, target: build_loss, edge_type: data_flow } + - { source: t_soi, target: build_loss, edge_type: external_source } + - { source: t_census, target: build_loss, edge_type: external_source } + - { source: t_cbo, target: build_loss, edge_type: external_source } + - { source: t_jct, target: build_loss, edge_type: external_source } + - { source: t_state, target: build_loss, edge_type: external_source } + - { source: build_loss, target: reweight, edge_type: data_flow, label: "(matrix, targets)" } + - { source: reweight, target: weight_validate, edge_type: data_flow } + - { source: weight_validate, target: out_enhanced, edge_type: produces_artifact } + - { source: util_loss, target: build_loss, edge_type: uses_utility } + - { source: util_l0_s3, target: reweight, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 4: Source Imputation (ACS + SIPP + SCF) + # ══════════════════════════════════════════════════════════════ + - id: 4 + label: "Stage 4" + title: "Stage 4: Source Imputation (ACS + SIPP + SCF)" + description: "Impute wealth/assets from external surveys onto stratified CPS via QRF" + country: us + extra_nodes: + - id: in_strat_s4 + label: "stratified_extended_cps_2024.h5" + node_type: input + description: "From Stage 3 (~12K HH)" + - id: in_acs_s4 + label: "ACS_2022" + node_type: input + description: "American Community Survey — has state_fips predictor" + - id: in_sipp_s4 + label: "SIPP 2023" + node_type: external + description: "pu2023_slim.csv from HuggingFace" + - id: in_scf_s4 + label: "SCF_2022" + node_type: input + description: "Survey of Consumer Finances — 50% random subsample" + - id: geo_assign_s4 + label: "Geography Assignment" + node_type: process + description: "assign_random_geography(n_clones=1) — block draw + state FIPS" + - id: acs_qrf + label: "ACS QRF Imputation" + node_type: process + description: "10 predictors incl. state_fips — imputes rent, real_estate_taxes" + - id: sipp_tips_qrf + label: "SIPP Tips QRF" + node_type: process + description: "4 predictors, NO state — imputes tip_income" + - id: sipp_assets_qrf + label: "SIPP Assets QRF" + node_type: process + description: "5 predictors — imputes bank_account_assets, stock_assets, bond_assets" + - id: scf_qrf + label: "SCF QRF Imputation" + node_type: process + description: "8 predictors — imputes net_worth, auto_loan_balance, auto_loan_interest" + - id: out_imputed + label: "source_imputed_stratified_extended_cps.h5" + node_type: output + description: "Enriched with ACS/SIPP/SCF vars — uploaded to HuggingFace" + - id: util_clone_assign + label: "clone_and_assign.py" + node_type: utility + description: "Geography assignment" + - id: util_qrf_s4 + label: "microimpute QRF" + node_type: utility + description: "fit_predict() for sequential imputation" + edges: + - { source: in_strat_s4, target: geo_assign_s4, edge_type: data_flow } + - { source: geo_assign_s4, target: acs_qrf, edge_type: data_flow, label: "state_fips" } + - { source: in_acs_s4, target: acs_qrf, edge_type: data_flow } + - { source: in_sipp_s4, target: sipp_tips_qrf, edge_type: external_source } + - { source: in_sipp_s4, target: sipp_assets_qrf, edge_type: external_source } + - { source: in_scf_s4, target: scf_qrf, edge_type: external_source } + - { source: acs_qrf, target: sipp_tips_qrf, edge_type: data_flow, label: "chain" } + - { source: sipp_tips_qrf, target: sipp_assets_qrf, edge_type: data_flow, label: "chain" } + - { source: sipp_assets_qrf, target: scf_qrf, edge_type: data_flow, label: "chain" } + - { source: scf_qrf, target: out_imputed, edge_type: produces_artifact } + - { source: util_clone_assign, target: geo_assign_s4, edge_type: uses_utility } + - { source: util_qrf_s4, target: acs_qrf, edge_type: uses_utility } + - { source: util_qrf_s4, target: sipp_tips_qrf, edge_type: uses_utility } + - { source: util_qrf_s4, target: sipp_assets_qrf, edge_type: uses_utility } + - { source: util_qrf_s4, target: scf_qrf, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 5: Matrix Build (Calibration Target Construction) + # ══════════════════════════════════════════════════════════════ + - id: 5 + label: "Stage 5" + title: "Stage 5: Matrix Build (Calibration Target Construction)" + description: "Build sparse calibration matrix (targets × households × clones)" + country: us + extra_nodes: + - id: in_cps_s5 + label: "source_imputed_stratified_extended_cps.h5" + node_type: input + description: "From Stage 4 (~12K HH)" + - id: in_db_s5 + label: "policy_data.db" + node_type: external + description: "SQLite calibration database — 10-step ETL" + - id: in_config_s5 + label: "target_config.yaml" + node_type: input + description: "Active target include list" + - id: in_blocks_s5 + label: "block_cd_distributions.csv.gz" + node_type: input + description: "Census block populations" + - id: target_resolve + label: "Target Resolution" + node_type: process + description: "SQL query to target_overview — ~8,000 rows total" + - id: target_uprate + label: "Target Uprating" + node_type: process + description: "CPI-U for dollars, pop growth for counts" + - id: geo_build + label: "Geography Index Build" + node_type: process + description: "state_to_cols, cd_to_cols maps" + - id: constraint_resolve + label: "Constraint Resolution" + node_type: process + description: "Non-geographic constraints from DB (age, medicaid, filer)" + - id: state_precomp + label: "Per-State Precomputation" + node_type: us_specific + description: "51 fresh Microsimulations — most expensive step" + - id: clone_assembly + label: "Clone Assembly" + node_type: process + description: "430 clones × 12K records = 5.16M columns" + - id: takeup_rerand + label: "Block-Level Takeup Re-randomization" + node_type: process + description: "Seeded on (block_geoid, hh_id) — ensures calibration consistency" + - id: sparse_build + label: "Sparse Matrix Construction" + node_type: process + description: "COO triples → CSR matrix — shape (n_targets, 5.16M), ~0.02% nonzero" + - id: out_pkg + label: "calibration_package.pkl" + node_type: output + description: "X_sparse CSR matrix, targets_df, initial_weights, metadata" + - id: util_sql + label: "sqlalchemy" + node_type: utility + description: "Database queries" + - id: util_pool + label: "ProcessPoolExecutor" + node_type: utility + description: "Parallel state computation" + - id: util_takeup_s5 + label: "compute_block_takeup_for_entities()" + node_type: utility + description: "utils/takeup.py" + - id: util_scipy + label: "scipy.sparse" + node_type: utility + description: "CSR/COO matrix construction" + edges: + - { source: in_cps_s5, target: target_resolve, edge_type: data_flow } + - { source: in_db_s5, target: target_resolve, edge_type: external_source, label: "SQL targets" } + - { source: in_config_s5, target: target_resolve, edge_type: data_flow, label: "include list" } + - { source: target_resolve, target: target_uprate, edge_type: data_flow } + - { source: target_uprate, target: geo_build, edge_type: data_flow } + - { source: geo_build, target: constraint_resolve, edge_type: data_flow } + - { source: constraint_resolve, target: state_precomp, edge_type: data_flow } + - { source: in_cps_s5, target: state_precomp, edge_type: data_flow, label: "household data" } + - { source: state_precomp, target: clone_assembly, edge_type: data_flow } + - { source: in_blocks_s5, target: clone_assembly, edge_type: data_flow, label: "block populations" } + - { source: clone_assembly, target: takeup_rerand, edge_type: data_flow } + - { source: takeup_rerand, target: sparse_build, edge_type: data_flow } + - { source: sparse_build, target: out_pkg, edge_type: produces_artifact } + - { source: util_sql, target: target_resolve, edge_type: uses_utility } + - { source: util_pool, target: state_precomp, edge_type: uses_utility } + - { source: util_takeup_s5, target: takeup_rerand, edge_type: uses_utility } + - { source: util_scipy, target: sparse_build, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 6: Weight Fitting (L0 Calibration) + # ══════════════════════════════════════════════════════════════ + - id: 6 + label: "Stage 6" + title: "Stage 6: Weight Fitting (L0 Calibration)" + description: "Fit log-weights using L0 HardConcrete gates on GPU" + country: us + extra_nodes: + - id: in_pkg_s6 + label: "calibration_package.pkl" + node_type: input + description: "From Stage 5 — X_sparse, targets_df, initial_weights" + - id: modal_gpu + label: "Modal GPU Container" + node_type: external + description: "T4 / A10 / A100 / H100 — 32GB RAM, 8 CPU" + - id: init_weights + label: "Compute Initial Weights" + node_type: process + description: "Population-proportional per CD" + - id: create_model + label: "Create SparseCalibrationWeights" + node_type: process + description: "n_features = 5.16M, init_keep_prob = 0.999" + - id: fit_model + label: "model.fit()" + node_type: process + description: "Adam optimizer — loss = RSE + λ₀·L0(w) + λ₂·‖w‖²" + - id: extract_weights + label: "Extract Weights" + node_type: process + description: "Deterministic gate threshold — produces exact zeros" + - id: out_weights + label: "calibration_weights.npy" + node_type: output + description: "Shape: (n_records × n_clones) — most entries zero" + - id: out_geo_s6 + label: "geography.npz" + node_type: output + description: "block_geoid, cd_geoid, county_fips, state_fips" + - id: out_diag + label: "unified_diagnostics.csv" + node_type: output + description: "Per-target error analysis" + - id: out_config_s6 + label: "unified_run_config.json" + node_type: output + description: "Hyperparameters + SHA256 checksums" + - id: util_l0 + label: "l0-python" + node_type: utility + description: "SparseCalibrationWeights — HardConcrete gates" + - id: util_pytorch + label: "PyTorch" + node_type: utility + description: "Adam optimizer, autograd, CUDA" + edges: + - { source: in_pkg_s6, target: init_weights, edge_type: data_flow } + - { source: init_weights, target: create_model, edge_type: data_flow } + - { source: create_model, target: fit_model, edge_type: data_flow } + - { source: modal_gpu, target: fit_model, edge_type: runs_on_infra, label: "runs on" } + - { source: fit_model, target: extract_weights, edge_type: data_flow } + - { source: extract_weights, target: out_weights, edge_type: produces_artifact } + - { source: extract_weights, target: out_geo_s6, edge_type: produces_artifact } + - { source: fit_model, target: out_diag, edge_type: produces_artifact } + - { source: fit_model, target: out_config_s6, edge_type: produces_artifact } + - { source: util_l0, target: create_model, edge_type: uses_utility } + - { source: util_pytorch, target: fit_model, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 7: Local Area H5 Build + # ══════════════════════════════════════════════════════════════ + - id: 7 + label: "Stage 7" + title: "Stage 7: Local Area H5 Build" + description: "Build 51 state + 435 district + 1 city H5 files on Modal workers" + country: us + extra_nodes: + - id: in_weights_s7 + label: "calibration_weights.npy" + node_type: input + description: "From Stage 6" + - id: in_dataset_s7 + label: "source_imputed_stratified_extended_cps.h5" + node_type: input + description: "From Stage 4" + - id: in_db_s7 + label: "policy_data.db" + node_type: external + description: "CD list for partitioning" + - id: modal_coord + label: "Modal Coordinator" + node_type: external + description: "coordinate_publish() — 8GB RAM, 24hr timeout" + - id: partition + label: "Partition Work" + node_type: process + description: "50 workers — weights: state by CD count, city=11, district=1" + - id: phase1 + label: "Phase 1: States" + node_type: us_specific + description: "51 state H5 files (50 + DC) — workers in parallel" + - id: phase2 + label: "Phase 2: Districts" + node_type: us_specific + description: "~435 congressional district H5 files — workers in parallel" + - id: phase3 + label: "Phase 3: Cities" + node_type: us_specific + description: "NYC (5 counties, 13 CDs) — city probability filtering" + - id: build_h5 + label: "build_h5() — Core Logic" + node_type: process + description: "18-step H5 construction — load, reshape, filter, clone, derive geo, SPM, takeup, write" + - id: worker_s7 + label: "Modal Worker Container" + node_type: external + description: "16GB RAM, 1 CPU each, 8-hour timeout" + - id: geo_derive + label: "derive_geography_from_blocks()" + node_type: process + description: "15 geographic variables from block GEOID" + - id: spm_recalc + label: "SPM Threshold Recalculation" + node_type: process + description: "Local median rents, family composition, tenure type" + - id: takeup_apply + label: "Takeup Re-application" + node_type: process + description: "9 takeup variables — block-level seeded draws" + - id: out_states + label: "states/*.h5" + node_type: output + description: "51 files — AL.h5 through WY.h5" + - id: out_districts + label: "districts/*.h5" + node_type: output + description: "~435 files — NC-01.h5, CA-52.h5, ..." + - id: out_cities + label: "cities/*.h5" + node_type: output + description: "1 file — NYC.h5" + - id: out_manifest + label: "manifest.json" + node_type: output + description: "SHA256 checksums for all H5 files" + - id: util_build_h5 + label: "publish_local_area.build_h5()" + node_type: utility + description: "calibration/publish_local_area.py" + - id: util_takeup_s7 + label: "apply_block_takeup_to_arrays()" + node_type: utility + description: "utils/takeup.py" + edges: + - { source: in_weights_s7, target: partition, edge_type: data_flow } + - { source: in_dataset_s7, target: partition, edge_type: data_flow } + - { source: in_db_s7, target: partition, edge_type: external_source, label: "CD list" } + - { source: partition, target: phase1, edge_type: data_flow } + - { source: phase1, target: phase2, edge_type: data_flow } + - { source: phase2, target: phase3, edge_type: data_flow } + - { source: phase1, target: build_h5, edge_type: data_flow, label: "calls" } + - { source: phase2, target: build_h5, edge_type: data_flow, label: "calls" } + - { source: phase3, target: build_h5, edge_type: data_flow, label: "calls" } + - { source: build_h5, target: geo_derive, edge_type: data_flow } + - { source: geo_derive, target: spm_recalc, edge_type: data_flow } + - { source: spm_recalc, target: takeup_apply, edge_type: data_flow } + - { source: modal_coord, target: worker_s7, edge_type: runs_on_infra, label: "orchestrates" } + - { source: worker_s7, target: build_h5, edge_type: runs_on_infra, label: "runs" } + - { source: phase1, target: out_states, edge_type: produces_artifact } + - { source: phase2, target: out_districts, edge_type: produces_artifact } + - { source: phase3, target: out_cities, edge_type: produces_artifact } + - { source: build_h5, target: out_manifest, edge_type: produces_artifact } + - { source: util_build_h5, target: build_h5, edge_type: uses_utility } + - { source: util_takeup_s7, target: takeup_apply, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 8: Validation & Promotion + # ══════════════════════════════════════════════════════════════ + - id: 8 + label: "Stage 8" + title: "Stage 8: Validation & Promotion" + description: "7-layer validation, staging upload, atomic promotion to production" + country: us + extra_nodes: + - id: in_h5s + label: "51 state + 435 district + 1 city H5s" + node_type: input + description: "From Stage 7 (Modal volume)" + - id: in_db_s8 + label: "policy_data.db" + node_type: external + description: "Validation targets" + - id: v1 + label: "Layer 1: Manifest Verification" + node_type: process + description: "SHA256 checksums per file — catches corruption" + - id: v2 + label: "Layer 2: Structural Sanity" + node_type: process + description: "Weight non-negativity, entity ID uniqueness, no NaN/Inf, mapping integrity" + - id: v3 + label: "Layer 3: Target-Based Validation" + node_type: process + description: "Full Microsimulation per area — compare sim values to targets" + - id: v4 + label: "Layer 4: Smoke Test" + node_type: process + description: "Sum 20 key vars across 51 states — GDP ~$29T, pop ~335M" + - id: v5 + label: "Layer 5: National H5 Validation" + node_type: process + description: ">30% deviation flagged — hardcoded reference values" + - id: v6 + label: "Layer 6: Pre-Upload Validation" + node_type: process + description: "File size minimums (100MB/50MB), H5 structure, Microsim aggregates" + - id: v7 + label: "Layer 7: Package Validation" + node_type: process + description: "Matrix achievability, target ratio analysis, zero-row detection" + - id: staging_upload + label: "Upload to Staging" + node_type: process + description: "upload_to_staging_hf() — batches of 50 files/commit" + - id: atomic_promote + label: "Atomic Promotion" + node_type: process + description: "promote_staging_to_production_hf() — single CommitOperationCopy commit" + - id: gcs_upload + label: "GCS Parallel Upload" + node_type: external + description: "gs://policyengine-us-data/ — version metadata on each blob" + - id: staging_cleanup + label: "Staging Cleanup" + node_type: process + description: "cleanup_staging_hf() — removes staging/ files" + - id: out_hf_prod + label: "HuggingFace Production" + node_type: external + description: "policyengine/policyengine-us-data — final published datasets" + - id: out_gcs + label: "Google Cloud Storage" + node_type: external + description: "gs://policyengine-us-data/ — CDN/backup" + - id: util_manifest_s8 + label: "manifest.py" + node_type: utility + description: "generate_manifest(), verify_manifest()" + - id: util_sanity + label: "sanity_checks.py" + node_type: utility + description: "run_sanity_checks()" + - id: util_validate + label: "validate_staging.py" + node_type: utility + description: "Full target comparison" + - id: util_upload + label: "data_upload.py" + node_type: utility + description: "staging/promote/cleanup" + edges: + - { source: in_h5s, target: v1, edge_type: data_flow } + - { source: in_db_s8, target: v3, edge_type: external_source, label: "targets" } + - { source: v1, target: v2, edge_type: data_flow } + - { source: v2, target: v3, edge_type: data_flow } + - { source: v3, target: v4, edge_type: data_flow } + - { source: v4, target: v5, edge_type: data_flow } + - { source: v5, target: v6, edge_type: data_flow } + - { source: v6, target: v7, edge_type: data_flow } + - { source: v7, target: staging_upload, edge_type: data_flow, label: "all pass" } + - { source: staging_upload, target: atomic_promote, edge_type: data_flow } + - { source: atomic_promote, target: gcs_upload, edge_type: data_flow } + - { source: gcs_upload, target: staging_cleanup, edge_type: data_flow } + - { source: atomic_promote, target: out_hf_prod, edge_type: produces_artifact } + - { source: gcs_upload, target: out_gcs, edge_type: produces_artifact } + - { source: util_manifest_s8, target: v1, edge_type: uses_utility } + - { source: util_sanity, target: v2, edge_type: uses_utility } + - { source: util_validate, target: v3, edge_type: uses_utility } + - { source: util_upload, target: staging_upload, edge_type: uses_utility } diff --git a/policyengine_us_data/datasets/cps/cps.py b/policyengine_us_data/datasets/cps/cps.py index 83eb8a7d1..ef573fda9 100644 --- a/policyengine_us_data/datasets/cps/cps.py +++ b/policyengine_us_data/datasets/cps/cps.py @@ -19,6 +19,8 @@ prioritize_reported_recipients, ) from policyengine_us_data.utils.randomness import seeded_rng +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode class CPS(Dataset): @@ -91,6 +93,13 @@ def generate(self): if self.frac is not None and self.frac < 1.0: self.downsample(frac=self.frac) + @pipeline_node(PipelineNode( + id="downsample", + label="Downsampling", + node_type="process", + description="frac=0.5 for CPS_2024 using Microsimulation.subsample()", + source_file="policyengine_us_data/datasets/cps/cps.py", + )) def downsample(self, frac: float): from policyengine_us import Microsimulation @@ -128,6 +137,14 @@ def downsample(self, frac: float): self.save_dataset(original_data) +@pipeline_node(PipelineNode( + id="add_rent", + label="Rent Imputation (QRF)", + node_type="process", + description="Impute rent and real estate taxes using QRF from ACS 2022", + details="10K sampled household heads as training data", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_rent(self, cps: h5py.File, person: DataFrame, household: DataFrame): cps["tenure_type"] = household.H_TENURE.map( { @@ -200,6 +217,14 @@ def add_rent(self, cps: h5py.File, person: DataFrame, household: DataFrame): cps["real_estate_taxes"][mask] = imputed_values["real_estate_taxes"] +@pipeline_node(PipelineNode( + id="add_takeup", + label="Benefit Takeup", + node_type="us_specific", + description="Stochastic takeup for 9 benefit programs", + details="SNAP, ACA, Medicaid, EITC, SSI, TANF, WIC, Head Start; state-specific Medicaid rates", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_takeup(self): data = self.load_dataset() @@ -359,6 +384,13 @@ def uprate_cps_data(data, from_period, to_period): return data +@pipeline_node(PipelineNode( + id="add_id_variables", + label="Add ID Variables", + node_type="process", + description="Create person_id, household_id, tax_unit_id, spm_unit_id, marital_unit_id", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_id_variables( cps: h5py.File, person: DataFrame, @@ -408,6 +440,14 @@ def add_id_variables( cps["marital_unit_id"] = marital_unit_id.drop_duplicates().values +@pipeline_node(PipelineNode( + id="add_personal_variables", + label="Add Personal Variables", + node_type="process", + description="Age, sex, disability, occupation, overtime flags", + details="80+ ages randomized to 80-84; 12 overtime occupation flags", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_personal_variables(cps: h5py.File, person: DataFrame) -> None: """Add personal demographic variables. @@ -482,6 +522,14 @@ def children_per_parent(col: str) -> pd.DataFrame: add_overtime_occupation(cps, person) +@pipeline_node(PipelineNode( + id="add_personal_income_variables", + label="Add Income Variables", + node_type="process", + description="30+ income types with splits", + details="SS classified by reason codes; retirement split by account type; capital gains 88% LT / 12% ST", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_personal_income_variables(cps: h5py.File, person: DataFrame, year: int): """Add income variables. @@ -739,6 +787,13 @@ def add_personal_income_variables(cps: h5py.File, person: DataFrame, year: int): cps[f"{var}_would_be_qualified"] = rng.random(len(person)) < prob +@pipeline_node(PipelineNode( + id="add_spm_variables", + label="SPM Variables", + node_type="process", + description="SPM thresholds and transfers (SNAP, housing, energy subsidies)", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_spm_variables(self, cps: h5py.File, spm_unit: DataFrame) -> None: from policyengine_us_data.utils.spm import ( calculate_spm_thresholds_with_geoadj, @@ -788,6 +843,13 @@ def add_spm_variables(self, cps: h5py.File, spm_unit: DataFrame) -> None: cps["reduced_price_school_meals_reported"] = cps["free_school_meals_reported"] * 0 +@pipeline_node(PipelineNode( + id="add_household_variables", + label="Household Variables", + node_type="process", + description="State FIPS, county FIPS, NYC flag from county codes", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_household_variables(cps: h5py.File, household: DataFrame) -> None: cps["state_fips"] = household.GESTFIPS cps["county_fips"] = household.GTCO @@ -811,6 +873,13 @@ def add_household_variables(cps: h5py.File, household: DataFrame) -> None: cps["in_nyc"] = np.isin(state_county_fips, nyc_full_county_fips) +@pipeline_node(PipelineNode( + id="add_previous_year_income", + label="Previous Year Income", + node_type="process", + description="Cross-year PERIDNUM linking for prior-year income", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_previous_year_income(self, cps: h5py.File) -> None: if self.previous_year_raw_cps is None: logging.info( @@ -871,6 +940,14 @@ def add_previous_year_income(self, cps: h5py.File) -> None: ].values +@pipeline_node(PipelineNode( + id="add_ssn_card_type", + label="SSN Card Type", + node_type="us_specific", + description="US immigration classification from 14 ASEC conditions", + details="Undocumented target: 13M; SSN card types 0-3", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_ssn_card_type( cps: h5py.File, person: pd.DataFrame, @@ -1753,6 +1830,14 @@ def _update_documentation_with_numbers(log_df, docs_dir): print(f"Documentation updated with population numbers: {doc_path}") +@pipeline_node(PipelineNode( + id="add_tips", + label="Tips Imputation (QRF)", + node_type="process", + description="Impute tip income and liquid assets from SIPP 2023", + details="Models cached as pickle; QRF fit_predict()", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_tips(self, cps: h5py.File): self.save_dataset(cps) from policyengine_us import Microsimulation @@ -1872,6 +1957,13 @@ def add_overtime_occupation(cps: h5py.File, person: DataFrame) -> None: ) +@pipeline_node(PipelineNode( + id="add_auto_loan", + label="Auto Loan / Net Worth (QRF)", + node_type="process", + description="Impute auto loan balance, interest, and net worth from SCF 2022", + source_file="policyengine_us_data/datasets/cps/cps.py", +)) def add_auto_loan_interest_and_net_worth(self, cps: h5py.File) -> None: """ "Add auto loan balance, interest and net_worth variable.""" self.save_dataset(cps) diff --git a/policyengine_us_data/datasets/puf/puf.py b/policyengine_us_data/datasets/puf/puf.py index baedce8a8..6957c2a85 100644 --- a/policyengine_us_data/datasets/puf/puf.py +++ b/policyengine_us_data/datasets/puf/puf.py @@ -15,6 +15,8 @@ from policyengine_us_data.utils.uprating import ( create_policyengine_uprating_factors_table, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode rng = np.random.default_rng(seed=64) @@ -52,6 +54,13 @@ def conditionally_sample_lognormal(flag, target_mean, log_sigma, rng): ) +@pipeline_node(PipelineNode( + id="simulate_qbi", + label="QBI Simulation", + node_type="process", + description="Simulate W-2 wages, UBIA, and SSTB for Section 199A", + source_file="policyengine_us_data/datasets/puf/puf.py", +)) def simulate_w2_and_ubia_from_puf(puf, *, seed=None, diagnostics=True): """ Simulate two Section 199A guard-rail quantities for every record @@ -201,6 +210,13 @@ def impute_pension_contributions_to_puf(puf_df): return predictions["pre_tax_contributions"] +@pipeline_node(PipelineNode( + id="impute_puf_demographics", + label="Impute PUF Demographics", + node_type="process", + description="QRF imputation for age, gender, and earnings split", + source_file="policyengine_us_data/datasets/puf/puf.py", +)) def impute_missing_demographics( puf: pd.DataFrame, demographics: pd.DataFrame ) -> pd.DataFrame: @@ -308,6 +324,14 @@ def decode_age_dependent(age_range: int) -> int: return rng.integers(low=lower, high=upper, endpoint=False) +@pipeline_node(PipelineNode( + id="preprocess_puf", + label="Preprocess PUF", + node_type="process", + description="Rename 60+ IRS variables to PolicyEngine names", + details="E00200 → employment_income, etc.", + source_file="policyengine_us_data/datasets/puf/puf.py", +)) def preprocess_puf(puf: pd.DataFrame) -> pd.DataFrame: # Add variable renames puf.S006 = puf.S006 / 100 diff --git a/policyengine_us_data/pipeline_metadata.py b/policyengine_us_data/pipeline_metadata.py new file mode 100644 index 000000000..9e17d0b12 --- /dev/null +++ b/policyengine_us_data/pipeline_metadata.py @@ -0,0 +1,40 @@ +"""Pipeline metadata decorators. + +Attach pipeline documentation to functions without changing their behavior. +The Griffe extraction script reads these decorators statically (via AST) +to generate pipeline.json for visualization. + +Usage: + from policyengine_us_data.pipeline_metadata import pipeline_node + from policyengine_us_data.pipeline_schema import PipelineNode + + @pipeline_node(PipelineNode( + id="add_rent", + label="Rent Imputation (QRF)", + node_type="process", + description="Impute rent and real estate taxes using QRF from ACS", + )) + def add_rent(self, cps, person, household): + ... +""" + +from policyengine_us_data.pipeline_schema import PipelineNode + + +def pipeline_node(node: PipelineNode): + """Decorator that attaches pipeline node metadata to a function. + + Does not modify the function's behavior. The metadata is stored + as func._pipeline_node and read by the Griffe extension during + static analysis. + + Args: + node: A PipelineNode describing this function's role in the + pipeline — its type, description, and visual properties. + """ + + def wrapper(func): + func._pipeline_node = node + return func + + return wrapper diff --git a/policyengine_us_data/pipeline_schema.py b/policyengine_us_data/pipeline_schema.py new file mode 100644 index 000000000..9192b6d7d --- /dev/null +++ b/policyengine_us_data/pipeline_schema.py @@ -0,0 +1,117 @@ +"""Pipeline documentation schema. + +Defines typed dataclasses for pipeline nodes, edges, and stages. +Used by @pipeline_node decorators to annotate source code and by +the Griffe extraction script to generate pipeline.json. + +Node and edge types match the visual taxonomy in +docs/pipeline-diagrams/CLAUDE.md and components/legends.js. +""" + +from dataclasses import dataclass, field +from typing import Literal + +NodeType = Literal[ + "input", + "output", + "process", + "utility", + "external", + "us_specific", + "uk_specific", + "missing", + "absent", +] + +EdgeType = Literal[ + "data_flow", + "produces_artifact", + "uses_utility", + "external_source", + "runs_on_infra", + "informational", +] + +# Node type → fill color, border color (matching legends.js) +NODE_COLORS = { + "input": {"fill": "#dbeafe", "border": "#3b82f6"}, + "output": {"fill": "#dcfce7", "border": "#22c55e"}, + "process": {"fill": "#ffedd5", "border": "#f97316"}, + "utility": {"fill": "#f3e8ff", "border": "#a855f7"}, + "external": {"fill": "#fef9c3", "border": "#eab308"}, + "us_specific": {"fill": "#fce7f3", "border": "#ec4899"}, + "uk_specific": {"fill": "#ccfbf1", "border": "#14b8a6"}, + "missing": {"fill": "#fee2e2", "border": "#ef4444"}, + "absent": {"fill": "#f3f4f6", "border": "#d1d5db"}, +} + +# Edge type → color, style, width (matching legends.js) +EDGE_STYLES = { + "data_flow": {"color": "#334155", "style": "solid", "width": 2}, + "produces_artifact": {"color": "#16a34a", "style": "solid", "width": 2}, + "uses_utility": {"color": "#7c3aed", "style": "dashed", "width": 1.5}, + "external_source": {"color": "#b45309", "style": "dotted", "width": 1.5}, + "runs_on_infra": {"color": "#dc2626", "style": "dashed", "width": 1.5}, + "informational": {"color": "#9ca3af", "style": "dotted", "width": 1}, +} + + +@dataclass +class PipelineNode: + """A node in a pipeline stage diagram. + + Args: + id: Unique identifier (snake_case, e.g., "add_rent"). + label: Display name (e.g., "Rent Imputation (QRF)"). + node_type: Visual category from NodeType. + description: One-line summary shown in tooltips. + details: Multi-line implementation notes shown in detail panel. + source_file: Python file path relative to repo root. + """ + + id: str + label: str + node_type: NodeType + description: str = "" + details: str = "" + source_file: str = "" + + +@dataclass +class PipelineEdge: + """A directed edge between two pipeline nodes. + + Args: + source: Source node ID. + target: Target node ID. + edge_type: Visual category from EdgeType. + label: Artifact name or data description on the edge. + """ + + source: str + target: str + edge_type: EdgeType = "data_flow" + label: str = "" + + +@dataclass +class PipelineStage: + """A stage in the pipeline (e.g., Stage 1: Base Dataset Construction). + + Args: + id: Stage number (0-8). + label: Short label (e.g., "Stage 1"). + title: Full title (e.g., "Stage 1: Base Dataset Construction"). + description: What this stage does. + country: Which country pipeline ("us" or "uk"). + nodes: Nodes in this stage. + edges: Edges within this stage. + """ + + id: int + label: str + title: str + description: str + country: str = "us" + nodes: list[PipelineNode] = field(default_factory=list) + edges: list[PipelineEdge] = field(default_factory=list) diff --git a/scripts/extract_pipeline.py b/scripts/extract_pipeline.py new file mode 100644 index 000000000..5dbee5bf9 --- /dev/null +++ b/scripts/extract_pipeline.py @@ -0,0 +1,248 @@ +"""Extract pipeline metadata from decorated source code and YAML manifest. + +Reads @pipeline_node decorators from Python source files via AST (no imports), +merges with pipeline_stages.yaml for stage groupings and edges, validates +the schema, and exports pipeline.json for React visualization. + +Usage: + python scripts/extract_pipeline.py + python scripts/extract_pipeline.py --output docs/pipeline-diagrams/pipeline.json +""" + +import ast +import json +import sys +from dataclasses import asdict +from datetime import datetime, timezone +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parent.parent +STAGES_YAML = REPO_ROOT / "pipeline_stages.yaml" +DEFAULT_OUTPUT = REPO_ROOT / "docs" / "pipeline-diagrams" / "pipeline.json" + +# Valid node and edge types (from pipeline_schema.py) +VALID_NODE_TYPES = { + "input", "output", "process", "utility", "external", + "us_specific", "uk_specific", "missing", "absent", +} +VALID_EDGE_TYPES = { + "data_flow", "produces_artifact", "uses_utility", + "external_source", "runs_on_infra", "informational", +} + + +def extract_pipeline_nodes_from_file(filepath: Path) -> list[dict]: + """Parse a Python file's AST and extract @pipeline_node decorator data. + + Returns a list of node dicts extracted from PipelineNode(...) calls + in decorators, without importing the module. + """ + try: + source = filepath.read_text() + tree = ast.parse(source, filename=str(filepath)) + except SyntaxError as e: + print(f" WARNING: Syntax error in {filepath}: {e}", file=sys.stderr) + return [] + + nodes = [] + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + + for decorator in node.decorator_list: + if not isinstance(decorator, ast.Call): + continue + + # Check if it's @pipeline_node(...) + func = decorator.func + name = "" + if isinstance(func, ast.Name): + name = func.id + elif isinstance(func, ast.Attribute): + name = func.attr + + if name != "pipeline_node": + continue + + # Extract the PipelineNode(...) argument + if not decorator.args: + continue + + arg = decorator.args[0] + if not isinstance(arg, ast.Call): + continue + + # Parse keyword arguments from PipelineNode(...) + node_data = {} + for kw in arg.keywords: + try: + node_data[kw.arg] = ast.literal_eval(kw.value) + except (ValueError, TypeError): + node_data[kw.arg] = str(ast.dump(kw.value)) + + if "id" in node_data: + nodes.append(node_data) + + return nodes + + +def scan_source_files(source_dir: Path) -> dict[str, list[dict]]: + """Scan all Python files under source_dir for @pipeline_node decorators. + + Returns a dict mapping relative file paths to lists of node dicts. + """ + results = {} + for py_file in sorted(source_dir.rglob("*.py")): + rel_path = str(py_file.relative_to(REPO_ROOT)) + nodes = extract_pipeline_nodes_from_file(py_file) + if nodes: + results[rel_path] = nodes + return results + + +def load_stages_yaml() -> dict: + """Load pipeline_stages.yaml manifest.""" + with open(STAGES_YAML) as f: + return yaml.safe_load(f) + + +def build_pipeline_json(output_path: Path = DEFAULT_OUTPUT): + """Build the full pipeline.json from source code + YAML manifest.""" + print("Extracting pipeline metadata...") + + # Step 1: Scan source code for @pipeline_node decorators + source_dir = REPO_ROOT / "policyengine_us_data" + file_nodes = scan_source_files(source_dir) + + total_nodes = sum(len(v) for v in file_nodes.values()) + print(f" Found {total_nodes} @pipeline_node decorators across {len(file_nodes)} files") + + # Build a lookup: node_id → node_data + all_code_nodes = {} + for filepath, nodes in file_nodes.items(): + for node in nodes: + node_id = node["id"] + if node_id in all_code_nodes: + print( + f" WARNING: Duplicate node ID '{node_id}' in {filepath} " + f"(already defined in {all_code_nodes[node_id].get('source_file', '?')})", + file=sys.stderr, + ) + all_code_nodes[node_id] = node + + # Step 2: Load YAML manifest + manifest = load_stages_yaml() + stages_data = manifest.get("stages", []) + + # Step 3: Merge code nodes with YAML stages + stages_output = [] + for stage_def in stages_data: + stage = { + "id": stage_def["id"], + "label": stage_def["label"], + "title": stage_def["title"], + "description": stage_def["description"], + "country": stage_def.get("country", "us"), + "nodes": [], + "edges": [], + } + + # Add extra nodes from YAML (inputs, outputs, utilities) + for extra in stage_def.get("extra_nodes", []): + node_type = extra.get("node_type", "input") + if node_type not in VALID_NODE_TYPES: + print( + f" WARNING: Invalid node_type '{node_type}' for node '{extra['id']}'", + file=sys.stderr, + ) + stage["nodes"].append(extra) + + # Add code-defined nodes that belong to this stage + # (matched by presence in the YAML edges as source or target) + edge_node_ids = set() + for edge in stage_def.get("edges", []): + edge_node_ids.add(edge["source"]) + edge_node_ids.add(edge["target"]) + + for node_id in edge_node_ids: + if node_id in all_code_nodes: + code_node = all_code_nodes[node_id] + # Don't duplicate if already in extra_nodes + existing_ids = {n["id"] for n in stage["nodes"]} + if node_id not in existing_ids: + stage["nodes"].append(code_node) + + # Add edges + for edge in stage_def.get("edges", []): + edge_type = edge.get("edge_type", "data_flow") + if edge_type not in VALID_EDGE_TYPES: + print( + f" WARNING: Invalid edge_type '{edge_type}' for edge " + f"'{edge['source']}' → '{edge['target']}'", + file=sys.stderr, + ) + stage["edges"].append(edge) + + stages_output.append(stage) + + # Step 4: Validate + errors = 0 + for stage in stages_output: + node_ids = {n["id"] for n in stage["nodes"]} + for edge in stage["edges"]: + if edge["source"] not in node_ids: + print( + f" ERROR: Edge source '{edge['source']}' not found in " + f"stage {stage['id']} nodes", + file=sys.stderr, + ) + errors += 1 + if edge["target"] not in node_ids: + print( + f" ERROR: Edge target '{edge['target']}' not found in " + f"stage {stage['id']} nodes", + file=sys.stderr, + ) + errors += 1 + + if errors: + print(f"\n {errors} validation error(s) found", file=sys.stderr) + + # Step 5: Build output + pipeline_json = { + "stages": stages_output, + "metadata": { + "generated_at": datetime.now(timezone.utc).isoformat(), + "total_nodes": sum(len(s["nodes"]) for s in stages_output), + "total_edges": sum(len(s["edges"]) for s in stages_output), + }, + } + + # Step 6: Write + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w") as f: + json.dump(pipeline_json, f, indent=2) + + print(f"\nWrote {output_path}") + print( + f" {len(stages_output)} stages, " + f"{pipeline_json['metadata']['total_nodes']} nodes, " + f"{pipeline_json['metadata']['total_edges']} edges" + ) + + return pipeline_json + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output", "-o", + default=str(DEFAULT_OUTPUT), + help="Output path for pipeline.json", + ) + args = parser.parse_args() + build_pipeline_json(Path(args.output))