diff --git a/.agents/skills/pipeline-doc-authoring/SKILL.md b/.agents/skills/pipeline-doc-authoring/SKILL.md new file mode 100644 index 00000000..5eb92706 --- /dev/null +++ b/.agents/skills/pipeline-doc-authoring/SKILL.md @@ -0,0 +1,69 @@ +--- +name: pipeline-doc-authoring +description: Add or update this repo's automated pipeline documentation for the US data build. Use when Codex needs to document a new pipeline step, refresh stale diagram metadata after code changes, add `@pipeline_node` decorators, update `pipeline_stages.yaml` edges or groups, regenerate `docs/pipeline-diagrams/app/pipeline.json`, or verify that the ReactFlow/ELK docs stay aligned with the underlying pipeline code. +--- + +# Pipeline Doc Authoring + +Use this skill to extend or repair the repository's automated pipeline documentation. The +documentation system is metadata-driven: code decorators describe process nodes, YAML describes +stage structure and edges, the extractor merges them into generated JSON, and the docs app renders +that JSON. + +## Workflow + +1. Map the real pipeline flow before editing docs. Read + [references/source-of-truth.md](references/source-of-truth.md) first. Then inspect the actual + driver method or orchestration path that runs the behavior you want to document. Derive order + from the code, not from old YAML or old rendered JSON. + +1. Choose the right documentation shape. + + - Use a normal decorated node for a pipeline-visible step that meaningfully transforms data or + produces an artifact. + - Use a YAML `extra_node` for fixed inputs, outputs, utilities, or external systems. + - Use a YAML `group` for orchestration wrappers whose important content is already expanded into + substeps. + - Do not create nodes for trivial helpers that are only implementation detail. + +1. Update code metadata. Add or refresh `@pipeline_node(PipelineNode(...))` on the implementing + function in its real source file. Keep the `id` stable and unique. Write `description` and + `details` from the current behavior, not from historical intent. + +1. Update stage structure in `pipeline_stages.yaml`. Add or update stage descriptions, + `extra_nodes`, `groups`, and `edges`. Edges control both graph order and node membership in a + stage. A decorated node will not render unless at least one stage edge references its `id`. + +1. Regenerate and validate. Run: + + - `python scripts/extract_pipeline.py` + - `python -m py_compile scripts/extract_pipeline.py ` + - `cd docs/pipeline-diagrams && npx tsc --noEmit` + - `git diff --check` + + Run `cd docs/pipeline-diagrams && npm run lint` when you touch the renderer. For pure metadata + changes, TypeScript plus extractor validation is usually enough. + +1. Review the generated diff semantically. Confirm that the new node or group appears in the + intended stage, with the intended neighbors, and that the stage description still matches the + real `generate()` or orchestration order. + +## Strong Patterns + +- Read [references/examples-and-pitfalls.md](references/examples-and-pitfalls.md) before adding a + new node type or stage pattern. +- Reuse a single decorated node ID across multiple stages only when the underlying function is + genuinely the same conceptual step in both places. +- Prefer wrapper groups over duplicate wrapper nodes when a function is just an orchestrator around + already-documented substeps. + +## Pitfalls + +- Do not edit `docs/pipeline-diagrams/app/pipeline.json` by hand. It is generated. +- Do not add a decorator without wiring the node into `pipeline_stages.yaml`; the extractor treats + unexpected unused decorators as errors. +- Do not fix stale docs by changing YAML alone when the code metadata is also wrong; update both. +- Do not add fake edges just to make a wrapper function visible. Use `groups` when the wrapper is a + visual boundary, not a data-flow step. +- Do not assume a clean textual rebase means the docs are aligned. Re-read the rebased code path + after merges to `main`. diff --git a/.agents/skills/pipeline-doc-authoring/agents/openai.yaml b/.agents/skills/pipeline-doc-authoring/agents/openai.yaml new file mode 100644 index 00000000..20a579ef --- /dev/null +++ b/.agents/skills/pipeline-doc-authoring/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Pipeline Doc Authoring" + short_description: "Update repo pipeline docs safely" + default_prompt: "Use $pipeline-doc-authoring to add or update automated pipeline documentation for this repository." diff --git a/.agents/skills/pipeline-doc-authoring/references/examples-and-pitfalls.md b/.agents/skills/pipeline-doc-authoring/references/examples-and-pitfalls.md new file mode 100644 index 00000000..c3c6cf41 --- /dev/null +++ b/.agents/skills/pipeline-doc-authoring/references/examples-and-pitfalls.md @@ -0,0 +1,105 @@ +# Examples and Pitfalls + +## Strong examples + +### Stage 2 Extended CPS process nodes + +- `policyengine_us_data/datasets/cps/extended_cps.py` + - `clone_features` + - `cps_only` + - `qrf_pass2` + - `formula_drop` + +Why these are good examples: + +- They document real pipeline-visible transformations. +- Their wording tracks the current code path rather than vague historical descriptions. +- Their stage order is enforced in `pipeline_stages.yaml`. + +### Shared node reused across stages + +- `policyengine_us_data/utils/mortgage_interest.py` + - `mortgage_convert` + +Why this is a good example: + +- The same decorated function is reused in both Stage 1 and Stage 2 by referencing the same node ID + in multiple stage edge sets. + +### Orchestration wrapper rendered as a group + +- `pipeline_stages.yaml` + - Stage `3b` group for `create_stratified_cps_dataset()` + - Stage `5` and `6` groups for `run_calibration()` + +Why this is a good example: + +- The wrapper is visible without creating fake data-flow nodes. +- The real substeps remain the actual graph nodes. + +### Local-area build orchestration + +- `policyengine_us_data/calibration/publish_local_area.py` + - `build_h5` + - `phase1` + - `phase2` + - `phase3` + +Why this is a good example: + +- It documents a multi-phase orchestrator while still allowing `main` to evolve helper behavior + underneath it. + +## Common pitfalls + +### Decorator added, node still missing + +Cause: + +- The node ID is not referenced by any stage edge in `pipeline_stages.yaml`. + +Fix: + +- Add the node to the correct stage by wiring its ID into one or more edges. + +### Wrapper function duplicated as a normal node + +Cause: + +- The function is only an orchestrator around already-expanded substeps. + +Fix: + +- Prefer a `group` unless the wrapper itself is a meaningful pipeline step. + +### Description is technically true but still stale + +Cause: + +- `main` changed the behavior inside the decorated function after the docs were first written. + +Fix: + +- Re-read the rebased code path and update `description`, `details`, and stage text together. + +### Visual renderer edited for a metadata problem + +Cause: + +- A missing node or wrong order is treated as a ReactFlow/ELK issue. + +Fix: + +- Fix decorators, YAML, or extractor output first. Touch renderer code only when the rendering model + itself is wrong. + +### Shared node reused incorrectly + +Cause: + +- The same node ID is reused across stages for two behaviors that have drifted apart. + +Fix: + +- Split into separate node IDs when the semantics differ, even if the implementation used to be + shared. diff --git a/.agents/skills/pipeline-doc-authoring/references/source-of-truth.md b/.agents/skills/pipeline-doc-authoring/references/source-of-truth.md new file mode 100644 index 00000000..cd7fe70d --- /dev/null +++ b/.agents/skills/pipeline-doc-authoring/references/source-of-truth.md @@ -0,0 +1,50 @@ +# Source of Truth + +Use these files in this order when updating pipeline docs. + +## Core contract + +- `policyengine_us_data/pipeline_metadata.py` + - Defines the `@pipeline_node` decorator used in source files. +- `policyengine_us_data/pipeline_schema.py` + - Defines the JSON-facing schema for stages, nodes, edges, and groups. + +## Extraction and validation + +- `scripts/extract_pipeline.py` + - Scans decorated source files. + - Merges code nodes with `pipeline_stages.yaml`. + - Fails on unexpected unused decorators. + - Contains the allowlist for intentionally omitted wrapper nodes. + +Important rule: stage edges determine which decorated nodes are included in a stage. If a node ID is +never referenced by an edge, it will not appear in the generated graph. + +## Stage structure + +- `pipeline_stages.yaml` + - Stage descriptions + - Manual inputs/outputs/utilities + - Data-flow and utility edges + - Visual wrapper groups + +## Generated artifact + +- `docs/pipeline-diagrams/app/pipeline.json` + - Generated output consumed by the docs app + - Never edit by hand + +## Docs app + +- `docs/pipeline-diagrams/app/components/PipelineDiagram.tsx` + - Renders flat nodes plus wrapper groups + - Most metadata-only changes should not require edits here +- `docs/pipeline-diagrams/README.md` + - Operator workflow for regeneration and checks + +## Validation commands + +- `python scripts/extract_pipeline.py` +- `python -m py_compile scripts/extract_pipeline.py ` +- `cd docs/pipeline-diagrams && npx tsc --noEmit` +- `git diff --check` diff --git a/.github/commit-pipeline-diagram-json.sh b/.github/commit-pipeline-diagram-json.sh new file mode 100644 index 00000000..ce35efc9 --- /dev/null +++ b/.github/commit-pipeline-diagram-json.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -euo pipefail + +PIPELINE_JSON="docs/pipeline-diagrams/app/pipeline.json" + +append_summary() { + if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + { + echo "## Pipeline Diagram Docs" + echo + echo "$1" + } >> "$GITHUB_STEP_SUMMARY" + fi +} + +if git diff --quiet -- "$PIPELINE_JSON"; then + append_summary "No generated pipeline JSON 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 "$PIPELINE_JSON" +git commit -m "Auto-update pipeline JSON" +git push origin HEAD:main + +append_summary "Updated \`$PIPELINE_JSON\` on \`main\`. Connected Vercel deployment will pick up that commit." diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 05cf169e..b6cf3028 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -7,6 +7,7 @@ on: jobs: lint: runs-on: ubuntu-latest + if: github.event.head_commit.message != 'Auto-update pipeline JSON' steps: - uses: actions/checkout@v4 - run: pip install ruff>=0.9.0 @@ -16,7 +17,7 @@ jobs: build-and-test: runs-on: ubuntu-latest needs: lint - if: github.event.head_commit.message != 'Update package version' + if: github.event.head_commit.message != 'Update package version' && github.event.head_commit.message != 'Auto-update pipeline JSON' env: MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }} MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} @@ -36,7 +37,7 @@ jobs: # ── Documentation ────────────────────────────────────────── docs: runs-on: ubuntu-latest - if: github.event.head_commit.message != 'Update package version' + if: github.event.head_commit.message != 'Update package version' && github.event.head_commit.message != 'Auto-update pipeline JSON' permissions: contents: write steps: @@ -63,7 +64,7 @@ jobs: # ── Versioning (bump + changelog on non-version-bump pushes) ── versioning: runs-on: ubuntu-latest - if: github.event.head_commit.message != 'Update package version' + if: github.event.head_commit.message != 'Update package version' && github.event.head_commit.message != 'Auto-update pipeline JSON' steps: - name: Generate GitHub App token id: app-token @@ -92,6 +93,58 @@ jobs: add: "." message: Update package version + # ── Pipeline diagram sync (after versioning) ─────────────── + pipeline-diagram-sync: + runs-on: ubuntu-latest + needs: versioning + if: needs.versioning.result == 'success' && github.event.head_commit.message != 'Update package version' && github.event.head_commit.message != 'Auto-update pipeline JSON' + permissions: + contents: write + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Refresh to latest main after versioning + run: | + git fetch origin main + git checkout --detach FETCH_HEAD + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install extractor dependencies + run: python -m pip install PyYAML + + - uses: actions/setup-node@v4 + with: + node-version: "24" + cache: "npm" + cache-dependency-path: docs/pipeline-diagrams/package-lock.json + + - name: Rebuild pipeline JSON + run: python scripts/extract_pipeline.py + + - name: Install diagram app dependencies + working-directory: docs/pipeline-diagrams + run: npm ci + + - name: Build pipeline diagram docs + working-directory: docs/pipeline-diagrams + run: npm run build + + - name: Commit pipeline diagram JSON if changed + run: bash .github/commit-pipeline-diagram-json.sh + # ── PyPI publish (version bump commits only) ──────────────── publish: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 86f3611a..c7edf910 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all format test test-unit test-integration install download upload docker documentation data validate-data calibrate calibrate-build publish-local-area upload-calibration upload-dataset 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-dataset promote build-h5s validate-local refresh-soi-targets push-pr-branch +.PHONY: all format test test-unit test-integration 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 refresh-soi-targets push-pr-branch diagrams-install diagrams SOI_SOURCE_YEAR ?= 2021 SOI_TARGET_YEAR ?= 2023 @@ -298,3 +298,9 @@ 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-install: + cd docs/pipeline-diagrams && npm install + +diagrams: + cd docs/pipeline-diagrams && npx next dev diff --git a/changelog.d/pipeline-diagrams.changed.md b/changelog.d/pipeline-diagrams.changed.md new file mode 100644 index 00000000..6c637bc9 --- /dev/null +++ b/changelog.d/pipeline-diagrams.changed.md @@ -0,0 +1 @@ +Expanded the automated pipeline documentation diagrams to cover clone-feature rematching, structural mortgage conversion, and wrapper-group steps in the US data build. diff --git a/docs/internals/calibration_package_internals.ipynb b/docs/internals/calibration_package_internals.ipynb index dee8a93a..1a0e6e04 100644 --- a/docs/internals/calibration_package_internals.ipynb +++ b/docs/internals/calibration_package_internals.ipynb @@ -53,7 +53,7 @@ " STATE_CODES,\n", ")\n", "\n", - "# Toy parameters \u2014 used in place of a real CPS H5 to avoid the multi-minute\n", + "# Toy parameters — used in place of a real CPS H5 to avoid the multi-minute\n", "# runtime of build_matrix() (runs PolicyEngine per state, per clone).\n", "N_CLONES = 3\n", "n_records = 8" @@ -201,7 +201,7 @@ "source": [ "## 1.3 Anatomy of a row\n", "\n", - "Each row is one calibration target \u2014 a known aggregate (dollar total, household count, person count) that the optimizer tries to match. The row vector's non-zero entries identify which cloned records can contribute to that target." + "Each row is one calibration target — a known aggregate (dollar total, household count, person count) that the optimizer tries to match. The row vector's non-zero entries identify which cloned records can contribute to that target." ] }, { @@ -431,9 +431,9 @@ "Example SNAP-contributing household: base record index 0\n", "\n", "Column positions across 3 clones:\n", - " col 0: CA (state=6, CD=601) \u2014 9 non-zero rows\n", - " col 8: CA (state=6, CD=601) \u2014 9 non-zero rows\n", - " col 16: CA (state=6, CD=601) \u2014 9 non-zero rows\n" + " col 0: CA (state=6, CD=601) — 9 non-zero rows\n", + " col 8: CA (state=6, CD=601) — 9 non-zero rows\n", + " col 16: CA (state=6, CD=601) — 9 non-zero rows\n" ] } ], @@ -459,7 +459,7 @@ " cd = geography.cd_geoid[col]\n", " nnz = X_sparse[:, col].nnz\n", " abbr = STATE_CODES.get(state, \"??\")\n", - " print(f\" col {col}: {abbr} (state={state}, CD={cd}) \u2014 {nnz} non-zero rows\")" + " print(f\" col {col}: {abbr} (state={state}, CD={cd}) — {nnz} non-zero rows\")" ] }, { @@ -505,7 +505,7 @@ "print(f\"Sparsity: {1 - density:.4%}\")\n", "\n", "nnz_per_row = np.diff(X_sparse.indptr)\n", - "print(f\"\\nNon-zeros per row:\")\n", + "print(\"\\nNon-zeros per row:\")\n", "print(f\" min: {nnz_per_row.min():,}\")\n", "print(f\" median: {int(np.median(nnz_per_row)):,}\")\n", "print(f\" mean: {nnz_per_row.mean():,.0f}\")\n", @@ -536,13 +536,13 @@ "\n", "### Group-based filtering (not used in production)\n", "\n", - "`drop_target_groups()` removes entire groups created by `create_target_groups()` from the matrix after it has been built. This was designed for cases where a group is redundant after hierarchical uprating \u2014 for example, state-level SNAP Household Count becomes redundant once district-level targets were reconciled to sum to the state totals.\n", + "`drop_target_groups()` removes entire groups created by `create_target_groups()` from the matrix after it has been built. This was designed for cases where a group is redundant after hierarchical uprating — for example, state-level SNAP Household Count becomes redundant once district-level targets were reconciled to sum to the state totals.\n", "\n", "This mechanism is **not currently used in the pipeline** (`target_groups=None` is passed to the optimizer). It is preserved for potential future use with group-weighted loss balancing. The example below demonstrates how it works.\n", "\n", "### Domain filtering via `target_config.yaml` (used in production)\n", "\n", - "In production, target filtering happens **before the matrix is built**, not after. `target_config.yaml` is the authoritative gating list: any database target not matching an `include` entry is discarded by the matrix builder at query time (see section 4.4). This is how targets are added or removed from calibration \u2014 by editing the YAML config, not by dropping groups from an already-built matrix.\n", + "In production, target filtering happens **before the matrix is built**, not after. `target_config.yaml` is the authoritative gating list: any database target not matching an `include` entry is discarded by the matrix builder at query time (see section 4.4). This is how targets are added or removed from calibration — by editing the YAML config, not by dropping groups from an already-built matrix.\n", "\n", "The key difference: group-based filtering operates on the built matrix (post-hoc removal of rows), while `target_config.yaml` filtering prevents unwanted targets from entering the matrix in the first place.\n", "\n", @@ -603,7 +603,7 @@ "X_final = X_filtered[achievable_mask, :]\n", "print(f\"\\nFinal matrix shape: {X_final.shape}\")\n", "print(f\"Final non-zero entries: {X_final.nnz:,}\")\n", - "print(f\"This is what the optimizer receives.\")" + "print(\"This is what the optimizer receives.\")" ] }, { @@ -612,9 +612,9 @@ "metadata": {}, "source": [ "---\n", - "# Part 2: Calibration matrix assembly \u2014 per-state simulation\n", + "# Part 2: Calibration matrix assembly — per-state simulation\n", "\n", - "The previous section showed *what* the matrix contains. This section explains *how* `UnifiedMatrixBuilder` fills it in \u2014 specifically the per-state simulation pass, domain constraints that gate matrix rows, and the special treatment of county-dependent variables.\n", + "The previous section showed *what* the matrix contains. This section explains *how* `UnifiedMatrixBuilder` fills it in — specifically the per-state simulation pass, domain constraints that gate matrix rows, and the special treatment of county-dependent variables.\n", "\n", "Because the matrix builder runs inside worker processes and cannot share live Python objects across process boundaries, all simulation is done with picklable top-level functions rather than methods." ] @@ -626,7 +626,7 @@ "source": [ "## 2.1 Per-state simulation with parallel workers\n", "\n", - "Before the clone loop begins, the builder dispatches one simulation job per unique state FIPS that appears across all clone assignments. Each job runs in a `ProcessPoolExecutor` worker and calls `_compute_single_state()` \u2014 a module-level function (not a method) so it is picklable.\n", + "Before the clone loop begins, the builder dispatches one simulation job per unique state FIPS that appears across all clone assignments. Each job runs in a `ProcessPoolExecutor` worker and calls `_compute_single_state()` — a module-level function (not a method) so it is picklable.\n", "\n", "Inside `_compute_single_state()`, the worker:\n", "\n", @@ -637,7 +637,7 @@ "5. Calculates each constraint variable mapped to the `person` entity (constraints need person-level resolution).\n", "\n", "```python\n", - "# From unified_matrix_builder.py \u2014 _compute_single_state()\n", + "# From unified_matrix_builder.py — _compute_single_state()\n", "state_sim = Microsimulation(dataset=dataset_path)\n", "\n", "state_sim.set_input(\n", @@ -683,7 +683,7 @@ "hh_vars[var] = arr\n", "```\n", "\n", - "3. Evaluates domain constraints (\u00a72.4) and writes non-zero COO entries for every target row.\n", + "3. Evaluates domain constraints (§2.4) and writes non-zero COO entries for every target row.\n", "\n", "Column layout: `col_idx = clone_idx * n_records + record_idx`, so `col_start = clone_idx * n_records` and `col_end = col_start + n_records`." ] @@ -695,7 +695,7 @@ "source": [ "## 2.3 Domain constraints: gating which records contribute\n", "\n", - "Each target row in the matrix can have a *domain constraint* \u2014 a predicate that must be true for a record to contribute a non-zero value to that row. A few common examples:\n", + "Each target row in the matrix can have a *domain constraint* — a predicate that must be true for a record to contribute a non-zero value to that row. A few common examples:\n", "\n", "| Target | Constraint variable | Constraint | Meaning |\n", "|---|---|---|---|\n", @@ -705,7 +705,7 @@ "\n", "Constraints come from the `stratum_constraints` table in `policy_data.db`. Each target belongs to a stratum, and each stratum can have one or more constraints stored as `(constraint_variable, operation, value)` rows. The ETL scripts (`db/etl_*.py`) populate these constraints when they insert targets. The matrix builder retrieves them via `_get_stratum_constraints(stratum_id)` for each target, separates geographic constraints (like `state_fips` and `congressional_district_geoid`) from non-geographic ones, and passes the non-geo constraints into the clone loop.\n", "\n", - "During the clone loop, `_evaluate_constraints_standalone()` applies the predicate at person level and aggregates to household level via `.any()`. A record that does *not* satisfy the constraint contributes 0 to that matrix row \u2014 even if it has a non-zero value for the target variable. For the IRS filer-count targets, `tax_unit_is_filer` plays a similar role: only filer tax units appear in those rows.\n", + "During the clone loop, `_evaluate_constraints_standalone()` applies the predicate at person level and aggregates to household level via `.any()`. A record that does *not* satisfy the constraint contributes 0 to that matrix row — even if it has a non-zero value for the target variable. For the IRS filer-count targets, `tax_unit_is_filer` plays a similar role: only filer tax units appear in those rows.\n", "\n", "```python\n", "# From _evaluate_constraints_standalone()\n", @@ -734,11 +734,11 @@ "source": [ "## 2.4 Takeup re-randomization in the clone loop\n", "\n", - "Several programs in PolicyEngine require a *take-up* draw: a stochastic binary decision representing whether an eligible household actually participates. The draws must be **reproducible and consistent** across the matrix builder and the H5 builder \u2014 if the two builds use different draws, the matrix rows target a different subpopulation than what ends up in the H5, breaking calibration.\n", + "Several programs in PolicyEngine require a *take-up* draw: a stochastic binary decision representing whether an eligible household actually participates. The draws must be **reproducible and consistent** across the matrix builder and the H5 builder — if the two builds use different draws, the matrix rows target a different subpopulation than what ends up in the H5, breaking calibration.\n", "\n", "### Why takeup lives in the matrix builder\n", "\n", - "Takeup draws depend on geography \u2014 each entity's take-up rate is resolved from the state FIPS of the census block assigned to that clone. Since different clones of the same household land in different states, they can have different take-up rates and therefore different draws. This per-clone variation is what makes takeup a matrix-building concern rather than a dataset-building concern.\n", + "Takeup draws depend on geography — each entity's take-up rate is resolved from the state FIPS of the census block assigned to that clone. Since different clones of the same household land in different states, they can have different take-up rates and therefore different draws. This per-clone variation is what makes takeup a matrix-building concern rather than a dataset-building concern.\n", "\n", "### `SIMPLE_TAKEUP_VARS` and `TAKEUP_AFFECTED_TARGETS`\n", "\n", @@ -753,27 +753,27 @@ "\n", "Current entries cover: `snap` (spm_unit), `aca_ptc` (tax_unit), `dc_property_tax_credit` (tax_unit), `head_start` (person), `early_head_start` (person), `ssi` (person), `medicaid` (person), `tanf` (spm_unit), and `would_file_taxes_voluntarily` (tax_unit, no target).\n", "\n", - "`TAKEUP_AFFECTED_TARGETS` is derived automatically from `SIMPLE_TAKEUP_VARS` \u2014 it maps each calibration target that has a takeup variable to `{takeup_var, entity, rate_key}`. Before the clone loop begins, the matrix builder matches `TAKEUP_AFFECTED_TARGETS` against the actual target variables to build `affected_target_info`, which tells each clone worker which target rows need takeup blending.\n", + "`TAKEUP_AFFECTED_TARGETS` is derived automatically from `SIMPLE_TAKEUP_VARS` — it maps each calibration target that has a takeup variable to `{takeup_var, entity, rate_key}`. Before the clone loop begins, the matrix builder matches `TAKEUP_AFFECTED_TARGETS` against the actual target variables to build `affected_target_info`, which tells each clone worker which target rows need takeup blending.\n", "\n", "### Step 1: State precomputation (`_compute_single_state`)\n", "\n", "When `rerandomize_takeup=True`, each state's precomputation runs PolicyEngine multiple times:\n", "\n", - "1. **Baseline simulation** \u2014 computes household and person values with the dataset's original takeup values. These populate `hh` (for non-takeup-affected targets) and `person` (for constraint evaluation).\n", + "1. **Baseline simulation** — computes household and person values with the dataset's original takeup values. These populate `hh` (for non-takeup-affected targets) and `person` (for constraint evaluation).\n", "\n", - "2. **All-takeup-true simulation** \u2014 sets every `SIMPLE_TAKEUP_VARS` entry to `True`, clears formula caches, then recalculates each takeup-affected target at entity level. Stored in `entity_vals`. This gives the \"what would this entity's value be if it participated in every program?\" answer.\n", + "2. **All-takeup-true simulation** — sets every `SIMPLE_TAKEUP_VARS` entry to `True`, clears formula caches, then recalculates each takeup-affected target at entity level. Stored in `entity_vals`. This gives the \"what would this entity's value be if it participated in every program?\" answer.\n", "\n", - "3. **Would-file-false simulation** (tax_unit targets only) \u2014 sets `would_file_taxes_voluntarily = False`, clears caches, recalculates tax_unit targets. Stored in `entity_wf_false`. This gives the alternative value for non-filers.\n", + "3. **Would-file-false simulation** (tax_unit targets only) — sets `would_file_taxes_voluntarily = False`, clears caches, recalculates tax_unit targets. Stored in `entity_wf_false`. This gives the alternative value for non-filers.\n", "\n", "These precomputed values are shared read-only data for all clone workers.\n", "\n", - "### Step 2: Clone loop \u2014 drawing and blending\n", + "### Step 2: Clone loop — drawing and blending\n", "\n", "Within `_process_single_clone()`, takeup runs in two phases after assembling per-clone values:\n", "\n", - "**Phase 1 \u2014 Non-target draws (line 703):** Draws for variables where `target is None` \u2014 currently just `would_file_taxes_voluntarily`. These are computed via `compute_block_takeup_for_entities()` and stored in `wf_draws`, keyed by entity. They must be drawn before Phase 2 because tax_unit targets depend on them.\n", + "**Phase 1 — Non-target draws (line 703):** Draws for variables where `target is None` — currently just `would_file_taxes_voluntarily`. These are computed via `compute_block_takeup_for_entities()` and stored in `wf_draws`, keyed by entity. They must be drawn before Phase 2 because tax_unit targets depend on them.\n", "\n", - "**Phase 2 \u2014 Target value assembly (line 729):** For each takeup-affected target (e.g., `snap`, `aca_ptc`):\n", + "**Phase 2 — Target value assembly (line 729):** For each takeup-affected target (e.g., `snap`, `aca_ptc`):\n", "1. Retrieves the precomputed `entity_vals[tvar]` (all-takeup-true eligible value) from the state sim.\n", "2. For tax_unit targets: uses `np.where(wf_draws[\"tax_unit\"], entity_vals[tvar], entity_wf_false[tvar])` to select between the all-true value and the would-file-false value, based on the Phase 1 `would_file` draw.\n", "3. Draws a per-entity program takeup boolean via `compute_block_takeup_for_entities()`.\n", @@ -796,9 +796,9 @@ "\n", "This guarantees:\n", "\n", - "- **Same `(var_name, hh_id, clone_idx)`** \u2192 same RNG seed \u2192 same draw, regardless of call order or process.\n", - "- **Different households** \u2192 different seeds \u2192 independent draws.\n", - "- **Different clones of the same household** \u2192 different seeds \u2192 independent assignments across clones.\n", + "- **Same `(var_name, hh_id, clone_idx)`** → same RNG seed → same draw, regardless of call order or process.\n", + "- **Different households** → different seeds → independent draws.\n", + "- **Different clones of the same household** → different seeds → independent assignments across clones.\n", "\n", "**Critical correctness invariant:** The matrix builder and the H5 builder (`publish_local_area.py`) call `compute_block_takeup_for_entities()` independently, but they must pass the same `var_name`, the same `entity_hh_ids`, and the same `clone_idx` for each record. If either side passes a different `(hh_id, clone_idx)` combination, the draws diverge, and the matrix rows target a different subpopulation than what ends up in the H5.\n", "\n", @@ -813,7 +813,7 @@ "source": [ "### Determinism demo\n", "\n", - "The following cell verifies that `compute_block_takeup_for_entities()` produces identical draws regardless of call order \u2014 the invariant that makes the matrix builder and H5 builder consistent. The matrix builder calls it once per clone (passing a single clone's entities), while the H5 builder may call it with all clones at once. Both must produce the same draws." + "The following cell verifies that `compute_block_takeup_for_entities()` produces identical draws regardless of call order — the invariant that makes the matrix builder and H5 builder consistent. The matrix builder calls it once per clone (passing a single clone's entities), while the H5 builder may call it with all clones at once. Both must produce the same draws." ] }, { @@ -850,7 +850,7 @@ "# Each household has 1 spm_unit for simplicity\n", "hh_ids = np.array([101, 101, 202, 202, 303, 303, 404, 404], dtype=np.int64)\n", "clone_idxs = np.array([0, 1, 0, 1, 0, 1, 0, 1], dtype=np.int64)\n", - "# Census block GEOIDs \u2014 first two digits = state FIPS\n", + "# Census block GEOIDs — first two digits = state FIPS\n", "# Using state 06 (CA) for all, so rate is resolved from state code 'CA'\n", "blocks = np.array([\"060750001001001\"] * 8)\n", "\n", @@ -936,17 +936,17 @@ "source": [ "## 2.6 COO assembly: from clone entries to sparse matrix\n", "\n", - "After state precomputation, takeup, and constraint evaluation, each clone worker builds its contribution to the calibration matrix as COO (coordinate) entries \u2014 a list of `(row, col, value)` triples. This allows efficiently storing non-zero values without having to store a large number of zero-valued matrix cells.\n", + "After state precomputation, takeup, and constraint evaluation, each clone worker builds its contribution to the calibration matrix as COO (coordinate) entries — a list of `(row, col, value)` triples. This allows efficiently storing non-zero values without having to store a large number of zero-valued matrix cells.\n", "\n", "### Per-clone entry generation\n", "\n", "Inside `_process_single_clone()`, the final loop iterates over every target row:\n", "\n", - "1. **Geographic filtering** \u2014 determines which columns (records) are relevant. District targets use `cd_to_cols`, state targets use `state_to_cols`, national targets use all columns. Only columns belonging to the current clone slice (`col_start..col_end`) are retained.\n", + "1. **Geographic filtering** — determines which columns (records) are relevant. District targets use `cd_to_cols`, state targets use `state_to_cols`, national targets use all columns. Only columns belonging to the current clone slice (`col_start..col_end`) are retained.\n", "\n", - "2. **Value computation** \u2014 for count targets (`*_count`), calls `_calculate_target_values_standalone()` which counts entities satisfying the constraint. For dollar targets, multiplies the household variable value by the domain constraint mask. Both use caches (`count_cache`, `mask_cache`) keyed by constraint tuple to avoid redundant computation.\n", + "2. **Value computation** — for count targets (`*_count`), calls `_calculate_target_values_standalone()` which counts entities satisfying the constraint. For dollar targets, multiplies the household variable value by the domain constraint mask. Both use caches (`count_cache`, `mask_cache`) keyed by constraint tuple to avoid redundant computation.\n", "\n", - "3. **Nonzero filtering** \u2014 only entries where `value != 0` are emitted. This is where the matrix gets its sparsity: records outside the target's geography, failing the domain constraint, or with zero variable value produce no entry.\n", + "3. **Nonzero filtering** — only entries where `value != 0` are emitted. This is where the matrix gets its sparsity: records outside the target's geography, failing the domain constraint, or with zero variable value produce no entry.\n", "\n", "```python\n", "# Simplified from _process_single_clone()\n", @@ -991,7 +991,7 @@ ")\n", "```\n", "\n", - "The resulting matrix has shape `(n_targets, n_records * n_clones)` \u2014 rows are calibration targets, columns are cloned household records. This is what the L0 optimizer receives." + "The resulting matrix has shape `(n_targets, n_records * n_clones)` — rows are calibration targets, columns are cloned household records. This is what the L0 optimizer receives." ] }, { @@ -1003,7 +1003,7 @@ "\n", "## 3.1 `target_config.yaml` include/exclude rules\n", "\n", - "`target_config.yaml` controls which targets from the built matrix are passed to the L0 optimizer. It is applied **after** the matrix is built, not during construction \u2014 the full unfiltered calibration package is saved first (Step 6b in `run_calibration`), then `apply_target_config()` filters targets for fitting (Step 6c). This design lets the same expensive matrix package be reused with different configs without rebuilding.\n", + "`target_config.yaml` controls which targets from the built matrix are passed to the L0 optimizer. It is applied **after** the matrix is built, not during construction — the full unfiltered calibration package is saved first (Step 6b in `run_calibration`), then `apply_target_config()` filters targets for fitting (Step 6c). This design lets the same expensive matrix package be reused with different configs without rebuilding.\n", "\n", "The filtering works by matching rows in `targets_df` against include/exclude rules. If `include` rules are present, only matching targets survive. If `exclude` rules are also present, they remove from the included set. The corresponding rows in `X_sparse` and `target_names` are dropped in sync.\n", "\n", @@ -1033,7 +1033,7 @@ " - variable: aca_ptc\n", " geo_level: district\n", "\n", - " # ACA PTC at national level \u2014 only the domain-constrained target\n", + " # ACA PTC at national level — only the domain-constrained target\n", " - variable: aca_ptc\n", " geo_level: national\n", " domain_variable: aca_ptc\n", @@ -1043,11 +1043,11 @@ " geo_level: state\n", "```\n", "\n", - "The inline comments in `target_config.yaml` document why specific targets were removed (e.g., `# REMOVED: state_income_tax \u2014 ETL hardcodes $0 for WA and NH`).\n", + "The inline comments in `target_config.yaml` document why specific targets were removed (e.g., `# REMOVED: state_income_tax — ETL hardcodes $0 for WA and NH`).\n", "\n", "### Distinction from `target_filter`\n", "\n", - "The matrix builder's `_query_targets()` accepts a separate `target_filter` dict (populated from `--domain-variables` CLI args) that filters targets at DB query time \u2014 *during* the build. In the default pipeline this filter is empty, so all active targets from `policy_data.db` enter the matrix. `target_config.yaml` then selects the subset to optimize against." + "The matrix builder's `_query_targets()` accepts a separate `target_filter` dict (populated from `--domain-variables` CLI args) that filters targets at DB query time — *during* the build. In the default pipeline this filter is empty, so all active targets from `policy_data.db` enter the matrix. `target_config.yaml` then selects the subset to optimize against." ] }, { @@ -1056,7 +1056,7 @@ "source": [ "## 3.2 Achievable target filtering\n", "\n", - "After `target_config.yaml` filtering, a target is **achievable** only if at least one record in the matrix can contribute to it \u2014 i.e., the row sum is positive. Targets with row sum = 0 are impossible constraints: no combination of weights can make the weighted sum match a nonzero target value if every entry in that row is zero.\n", + "After `target_config.yaml` filtering, a target is **achievable** only if at least one record in the matrix can contribute to it — i.e., the row sum is positive. Targets with row sum = 0 are impossible constraints: no combination of weights can make the weighted sum match a nonzero target value if every entry in that row is zero.\n", "\n", "This check runs immediately before the optimizer call in `run_calibration()` (Step 7):\n", "\n", @@ -1112,7 +1112,7 @@ "metadata": {}, "outputs": [], "source": [ - "# \u2500\u2500\u2500 Part 4 setup \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n", + "# ─── Part 4 setup ──────────────────────────────────────────────────────────\n", "# The uprating walkthrough below requires a fully initialized UnifiedMatrixBuilder\n", "# and a Microsimulation. These load the full source-imputed H5 and policy_data.db.\n", "#\n", @@ -1137,7 +1137,7 @@ "except NameError:\n", " builder = None\n", " sim = None\n", - " print(\"Part 4: builder/sim not initialized \u2014 cells will skip gracefully.\")\n", + " print(\"Part 4: builder/sim not initialized — cells will skip gracefully.\")\n", " print(\"Uncomment the setup lines above and re-run to execute the full walkthrough.\")" ] }, @@ -1301,7 +1301,7 @@ " display(summary)\n", "else:\n", " raw = None\n", - " print(\"Skipping \u2014 builder not initialized.\")" + " print(\"Skipping — builder not initialized.\")" ] }, { @@ -1331,7 +1331,7 @@ " print(f\" {yr} -> 2024 ({kind}): {f:.6f}\")\n", "else:\n", " uprating_factors = None\n", - " print(\"Skipping \u2014 builder not initialized.\")" + " print(\"Skipping — builder not initialized.\")" ] }, { @@ -1342,7 +1342,7 @@ "\n", "For each (state, variable) pair within a domain:\n", "\n", - "- **HIF** = `state_original / sum(cd_originals)` \u2014 pure base-year correction\n", + "- **HIF** = `state_original / sum(cd_originals)` — pure base-year correction\n", "- **UF** = state-specific uprating factor:\n", " - For **ACA PTC**: loaded from `aca_ptc_multipliers_2022_2024.csv` (CMS/KFF enrollment data)\n", " - For other domains: national CPI/pop factors as fallback" @@ -1366,7 +1366,7 @@ " result = builder._apply_hierarchical_uprating(raw, DOMAINS, uprating_factors)\n", "else:\n", " result = None\n", - " print(\"Skipping \u2014 builder not initialized.\")" + " print(\"Skipping — builder not initialized.\")" ] }, { @@ -1430,7 +1430,7 @@ " print(\"\\nSNAP (HIF>1, UF=1.0):\")\n", " show_reconciliation(result, raw, \"snap\", sample_states)\n", "else:\n", - " print(\"Skipping \u2014 result not available.\")" + " print(\"Skipping — result not available.\")" ] }, { @@ -1501,7 +1501,7 @@ " + (\"ALL PASSED\" if all_ok else \"SOME FAILED\")\n", " )\n", "else:\n", - " print(\"Skipping \u2014 result not available.\")" + " print(\"Skipping — result not available.\")" ] }, { @@ -1513,7 +1513,7 @@ "\n", "## 5.1 Calibration package serialization\n", "\n", - "Building the calibration matrix is the most expensive step in the pipeline \u2014 it requires running PolicyEngine simulations for every state across all clones. To avoid rebuilding when experimenting with different `target_config.yaml` settings or hyperparameters, `run_calibration()` saves the **full unfiltered** matrix as a calibration package (Step 6b) before applying any config filtering.\n", + "Building the calibration matrix is the most expensive step in the pipeline — it requires running PolicyEngine simulations for every state across all clones. To avoid rebuilding when experimenting with different `target_config.yaml` settings or hyperparameters, `run_calibration()` saves the **full unfiltered** matrix as a calibration package (Step 6b) before applying any config filtering.\n", "\n", "`save_calibration_package()` serializes:\n", "\n", @@ -1539,7 +1539,7 @@ "\n", "1. Find all `person_count` targets where `domain_variable == \"age\"` and `geo_level == \"district\"`.\n", "2. For each congressional district, sum the age-bin target values to get the district's total population.\n", - "3. Identify which matrix columns (records) have nonzero entries in those target rows \u2014 these are the records active in that CD.\n", + "3. Identify which matrix columns (records) have nonzero entries in those target rows — these are the records active in that CD.\n", "4. Set each record's initial weight to `district_population / n_active_records`.\n", "\n", "```python\n", diff --git a/docs/internals/data_build_internals.ipynb b/docs/internals/data_build_internals.ipynb index 27ef4a62..8138f198 100644 --- a/docs/internals/data_build_internals.ipynb +++ b/docs/internals/data_build_internals.ipynb @@ -109,7 +109,6 @@ "import numpy as np\n", "import pandas as pd\n", "from policyengine_us_data.calibration.clone_and_assign import (\n", - " GeographyAssignment,\n", " assign_random_geography,\n", " double_geography_for_puf,\n", ")\n", @@ -134,7 +133,7 @@ "\n", "print(f\"n_records={geo.n_records}, n_clones={geo.n_clones}\")\n", "print(f\"Total column positions: {geo.n_records * geo.n_clones}\")\n", - "print(f\"\\nFirst 4 column positions:\")\n", + "print(\"\\nFirst 4 column positions:\")\n", "for i in range(4):\n", " print(\n", " f\" col {i}: block={geo.block_geoid[i]} cd={geo.cd_geoid[i]} state={geo.state_fips[i]}\"\n", diff --git a/docs/internals/optimization_and_local_dataset_assembly_internals.ipynb b/docs/internals/optimization_and_local_dataset_assembly_internals.ipynb index ddde0dba..176b5835 100644 --- a/docs/internals/optimization_and_local_dataset_assembly_internals.ipynb +++ b/docs/internals/optimization_and_local_dataset_assembly_internals.ipynb @@ -296,7 +296,6 @@ } ], "source": [ - "import torch\n", "import torch.nn as nn\n", "import numpy as np\n", "\n", @@ -853,7 +852,7 @@ " print(f\" Not achievable: {(~diag['achievable']).sum()}\")\n", "\n", " achievable = diag[diag[\"achievable\"]]\n", - " print(f\"\\nAmong achievable targets:\")\n", + " print(\"\\nAmong achievable targets:\")\n", " print(f\" Mean abs rel error: {achievable['abs_rel_error'].mean():.4%}\")\n", " print(f\" Max abs rel error: {achievable['abs_rel_error'].max():.4%}\")\n", " print(f\" Median: {achievable['abs_rel_error'].median():.4%}\")\n", diff --git a/docs/local_area_calibration_setup.ipynb b/docs/local_area_calibration_setup.ipynb index a230eba0..d013abc5 100644 --- a/docs/local_area_calibration_setup.ipynb +++ b/docs/local_area_calibration_setup.ipynb @@ -290,7 +290,7 @@ "source": [ "blocks, cds, states, probs = load_global_block_distribution()\n", "print(f\"Global block distribution: {len(blocks):,} blocks\")\n", - "print(f\"Top 5 states by total probability:\")\n", + "print(\"Top 5 states by total probability:\")\n", "state_prob = pd.Series(probs, index=states).groupby(level=0).sum()\n", "top5 = state_prob.nlargest(5)\n", "for fips, p in top5.items():\n", @@ -547,7 +547,7 @@ } ], "source": [ - "print(f\"Example household clone visibility:\\n\")\n", + "print(\"Example household clone visibility:\\n\")\n", "for c in range(N_CLONES):\n", " col = c * n_records + record_idx\n", " state = int(geography.state_fips[col])\n", @@ -671,10 +671,10 @@ "print(f\" {draws_a1}\")\n", "print(f\" {draws_a2}\")\n", "print(f\" Match: {np.allclose(draws_a1, draws_a2)}\")\n", - "print(f\"\\nDifferent block, same var:\")\n", + "print(\"\\nDifferent block, same var:\")\n", "print(f\" {draws_b}\")\n", "print(f\" Match: {np.allclose(draws_a1, draws_b)}\")\n", - "print(f\"\\nSame block, different var:\")\n", + "print(\"\\nSame block, different var:\")\n", "print(f\" {draws_other}\")\n", "print(f\" Match: {np.allclose(draws_a1, draws_other)}\")" ] @@ -845,7 +845,7 @@ } ], "source": [ - "print(f\"Example household non-zero pattern across clones:\\n\")\n", + "print(\"Example household non-zero pattern across clones:\\n\")\n", "for c in range(N_CLONES):\n", " col = c * n_records + record_idx\n", " col_vec = X_sparse[:, col]\n", @@ -1099,7 +1099,7 @@ "print(example_mapping.to_string(index=False))\n", "\n", "new_ids = example_mapping.new_household_id\n", - "print(f\"\\nIn stacked dataset:\\n\")\n", + "print(\"\\nIn stacked dataset:\\n\")\n", "print(hh_after_df.loc[hh_after_df.household_id.isin(new_ids)].to_string(index=False))" ] }, diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 93410385..00000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,1363 +0,0 @@ -{ - "name": "docs", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "docs", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "mermaid": "^11.9.0" - } - }, - "node_modules/@antfu/install-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", - "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", - "license": "MIT", - "dependencies": { - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@antfu/utils": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", - "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@braintree/sanitize-url": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", - "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", - "license": "MIT" - }, - "node_modules/@chevrotain/cst-dts-gen": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", - "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/gast": "11.0.3", - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/@chevrotain/gast": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", - "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/types": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/@chevrotain/regexp-to-ast": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", - "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/types": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", - "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", - "license": "Apache-2.0" - }, - "node_modules/@chevrotain/utils": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", - "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", - "license": "Apache-2.0" - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, - "node_modules/@iconify/utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", - "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", - "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^1.0.0", - "@antfu/utils": "^8.1.0", - "@iconify/types": "^2.0.0", - "debug": "^4.4.0", - "globals": "^15.14.0", - "kolorist": "^1.8.0", - "local-pkg": "^1.0.0", - "mlly": "^1.7.4" - } - }, - "node_modules/@mermaid-js/parser": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", - "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", - "license": "MIT", - "dependencies": { - "langium": "3.3.1" - } - }, - "node_modules/@types/d3": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", - "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/d3-axis": "*", - "@types/d3-brush": "*", - "@types/d3-chord": "*", - "@types/d3-color": "*", - "@types/d3-contour": "*", - "@types/d3-delaunay": "*", - "@types/d3-dispatch": "*", - "@types/d3-drag": "*", - "@types/d3-dsv": "*", - "@types/d3-ease": "*", - "@types/d3-fetch": "*", - "@types/d3-force": "*", - "@types/d3-format": "*", - "@types/d3-geo": "*", - "@types/d3-hierarchy": "*", - "@types/d3-interpolate": "*", - "@types/d3-path": "*", - "@types/d3-polygon": "*", - "@types/d3-quadtree": "*", - "@types/d3-random": "*", - "@types/d3-scale": "*", - "@types/d3-scale-chromatic": "*", - "@types/d3-selection": "*", - "@types/d3-shape": "*", - "@types/d3-time": "*", - "@types/d3-time-format": "*", - "@types/d3-timer": "*", - "@types/d3-transition": "*", - "@types/d3-zoom": "*" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", - "license": "MIT" - }, - "node_modules/@types/d3-axis": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", - "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-brush": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", - "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-chord": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", - "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", - "license": "MIT" - }, - "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-contour": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", - "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", - "license": "MIT" - }, - "node_modules/@types/d3-dispatch": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", - "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", - "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-dsv": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", - "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-fetch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", - "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", - "license": "MIT", - "dependencies": { - "@types/d3-dsv": "*" - } - }, - "node_modules/@types/d3-force": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", - "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", - "license": "MIT" - }, - "node_modules/@types/d3-format": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", - "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", - "license": "MIT" - }, - "node_modules/@types/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-hierarchy": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", - "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", - "license": "MIT" - }, - "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-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-polygon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", - "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", - "license": "MIT" - }, - "node_modules/@types/d3-quadtree": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", - "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", - "license": "MIT" - }, - "node_modules/@types/d3-random": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", - "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", - "license": "MIT" - }, - "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-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-time-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", - "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "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/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/chevrotain": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", - "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", - "license": "Apache-2.0", - "dependencies": { - "@chevrotain/cst-dts-gen": "11.0.3", - "@chevrotain/gast": "11.0.3", - "@chevrotain/regexp-to-ast": "11.0.3", - "@chevrotain/types": "11.0.3", - "@chevrotain/utils": "11.0.3", - "lodash-es": "4.17.21" - } - }, - "node_modules/chevrotain-allstar": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", - "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21" - }, - "peerDependencies": { - "chevrotain": "^11.0.0" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", - "license": "MIT" - }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "license": "MIT", - "dependencies": { - "layout-base": "^1.0.0" - } - }, - "node_modules/cytoscape": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.0.tgz", - "integrity": "sha512-2d2EwwhaxLWC8ahkH1PpQwCyu6EY3xDRdcEJXrLTb4fOUtVc+YWQalHU67rFS1a6ngj1fgv9dQLtJxP/KAFZEw==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "license": "MIT", - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "license": "MIT", - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "license": "MIT", - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", - "license": "MIT" - }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "license": "ISC", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "license": "ISC", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "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-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "license": "ISC", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "license": "ISC", - "dependencies": { - "delaunator": "5" - }, - "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-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "license": "ISC", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "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-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "license": "ISC", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "license": "ISC", - "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-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "license": "BSD-3-Clause" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC" - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "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-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "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/dagre-d3-es": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", - "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==", - "license": "MIT", - "dependencies": { - "d3": "^7.9.0", - "lodash-es": "^4.17.21" - } - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/dompurify": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", - "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, - "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", - "license": "MIT" - }, - "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hachure-fill": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", - "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", - "license": "MIT" - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/katex": { - "version": "0.16.22", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", - "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/katex/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" - }, - "node_modules/kolorist": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", - "license": "MIT" - }, - "node_modules/langium": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", - "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", - "license": "MIT", - "dependencies": { - "chevrotain": "~11.0.3", - "chevrotain-allstar": "~0.3.0", - "vscode-languageserver": "~9.0.1", - "vscode-languageserver-textdocument": "~1.0.11", - "vscode-uri": "~3.0.8" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", - "license": "MIT" - }, - "node_modules/local-pkg": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz", - "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==", - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.0.1", - "quansync": "^0.2.8" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/marked": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.1.1.tgz", - "integrity": "sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/mermaid": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.9.0.tgz", - "integrity": "sha512-YdPXn9slEwO0omQfQIsW6vS84weVQftIyyTGAZCwM//MGhPzL1+l6vO6bkf0wnP4tHigH1alZ5Ooy3HXI2gOag==", - "license": "MIT", - "dependencies": { - "@braintree/sanitize-url": "^7.0.4", - "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.6.2", - "@types/d3": "^7.4.3", - "cytoscape": "^3.29.3", - "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.2.0", - "d3": "^7.9.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.11", - "dayjs": "^1.11.13", - "dompurify": "^3.2.5", - "katex": "^0.16.22", - "khroma": "^2.1.0", - "lodash-es": "^4.17.21", - "marked": "^16.0.0", - "roughjs": "^4.6.6", - "stylis": "^4.3.6", - "ts-dedent": "^2.2.0", - "uuid": "^11.1.0" - } - }, - "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" - } - }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT" - }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", - "license": "MIT" - }, - "node_modules/path-data-parser": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", - "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/pkg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", - "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/points-on-curve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", - "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", - "license": "MIT" - }, - "node_modules/points-on-path": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", - "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", - "license": "MIT", - "dependencies": { - "path-data-parser": "0.1.0", - "points-on-curve": "0.2.0" - } - }, - "node_modules/quansync": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz", - "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], - "license": "MIT" - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", - "license": "Unlicense" - }, - "node_modules/roughjs": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", - "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", - "license": "MIT", - "dependencies": { - "hachure-fill": "^0.5.2", - "path-data-parser": "^0.1.0", - "points-on-curve": "^0.2.0", - "points-on-path": "^0.2.1" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "license": "MIT" - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "license": "MIT", - "engines": { - "node": ">=6.10" - } - }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "license": "MIT" - } - } -} diff --git a/docs/pipeline-diagrams/.gitignore b/docs/pipeline-diagrams/.gitignore new file mode 100644 index 00000000..5ef6a520 --- /dev/null +++ b/docs/pipeline-diagrams/.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/AGENTS.md b/docs/pipeline-diagrams/AGENTS.md new file mode 100644 index 00000000..ab26fd15 --- /dev/null +++ b/docs/pipeline-diagrams/AGENTS.md @@ -0,0 +1,9 @@ + + +# 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/CLAUDE.md b/docs/pipeline-diagrams/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/docs/pipeline-diagrams/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/docs/pipeline-diagrams/README.md b/docs/pipeline-diagrams/README.md new file mode 100644 index 00000000..d1f4fe5f --- /dev/null +++ b/docs/pipeline-diagrams/README.md @@ -0,0 +1,43 @@ +# Pipeline Diagrams + +Interactive ReactFlow documentation for the PolicyEngine US data pipeline. + +## Data Flow + +The rendered graph is generated from two sources: + +- `@pipeline_node(PipelineNode(...))` decorators in `policyengine_us_data/**/*.py` +- stage groupings, extra nodes, and edges in `pipeline_stages.yaml` + +Regenerate the app-consumed JSON from the repository root: + +```bash +python scripts/extract_pipeline.py +``` + +The extractor writes `docs/pipeline-diagrams/app/pipeline.json`. Do not edit that file by hand. + +## Local App + +Use Node.js 20.9 or newer. CI uses Node.js 24. + +```bash +cd docs/pipeline-diagrams +npm ci +npm run dev +``` + +Open http://localhost:3000/us. + +## Checks + +Run the generator and TypeScript check before committing diagram metadata: + +```bash +python scripts/extract_pipeline.py +cd docs/pipeline-diagrams +npx tsc --noEmit +``` + +`npm run lint` currently includes renderer cleanup work outside the generation path, so the +automated update workflow uses the TypeScript check only. diff --git a/docs/pipeline-diagrams/app/[country]/page.tsx b/docs/pipeline-diagrams/app/[country]/page.tsx new file mode 100644 index 00000000..3b0495fb --- /dev/null +++ b/docs/pipeline-diagrams/app/[country]/page.tsx @@ -0,0 +1,44 @@ +"use client"; + +import PipelineDiagram from "../components/PipelineDiagram"; +import pipelineData from "../pipeline.json"; + +const OVERVIEW_EDGES: Array<[number | string, number | string]> = [ + [0, 1], + [1, 2], + [2, "3a"], + [2, "3b"], + ["3b", 4], + [4, 5], + [5, 6], + [6, 7], + [7, 8], +]; + +const stageIds = new Set(pipelineData.stages.map((stage) => String(stage.id))); + +// Overview: show the real high-level branch structure. +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.title, + node_type: "process", + description: s.description, + })), + edges: OVERVIEW_EDGES.filter( + ([source, target]) => + stageIds.has(String(source)) && stageIds.has(String(target)) + ).map(([source, target]) => ({ + source: `stage_${source}`, + target: `stage_${target}`, + edge_type: "data_flow", + })), +}; + +export default function CountryOverview() { + return ; +} diff --git a/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.tsx b/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.tsx new file mode 100644 index 00000000..62c42beb --- /dev/null +++ b/docs/pipeline-diagrams/app/[country]/stage/[stageId]/page.tsx @@ -0,0 +1,24 @@ +"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 stage = pipelineData.stages.find((s) => String(s.id) === stageId); + + if (!stage) { + return ( +
+ Stage {stageId} not found. +
+ ); + } + + return ; +} diff --git a/docs/pipeline-diagrams/app/colors.ts b/docs/pipeline-diagrams/app/colors.ts new file mode 100644 index 00000000..abfdebaf --- /dev/null +++ b/docs/pipeline-diagrams/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/app/components/ElkEdge.tsx b/docs/pipeline-diagrams/app/components/ElkEdge.tsx new file mode 100644 index 00000000..92a00e2b --- /dev/null +++ b/docs/pipeline-diagrams/app/components/ElkEdge.tsx @@ -0,0 +1,127 @@ +"use client"; + +import { EdgeLabelRenderer, getSmoothStepPath, type EdgeProps } from "@xyflow/react"; + +type ElkLabelPosition = { + x: number; + y: number; + width?: number; + height?: number; +}; + +/** + * Custom edge that renders using ELK's computed edge section 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; + let elkLabel: ElkLabelPosition | null = null; + + 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 && endPoint ? [startPoint, ...bendPoints, endPoint] : []; + + if (points.length > 0) { + edgePath = points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" "); + + if (points.length === 2) { + labelX = (points[0].x + points[1].x) / 2; + labelY = (points[0].y + points[1].y) / 2; + } else { + const mid = Math.floor(points.length / 2); + labelX = points[mid].x; + labelY = points[mid].y; + } + } else { + const [path, lx, ly] = getSmoothStepPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + }); + edgePath = path; + labelX = lx; + labelY = ly; + } + elkLabel = (data.elkLabel as ElkLabelPosition | null) || null; + } 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/app/components/NodeDetailPanel.tsx b/docs/pipeline-diagrams/app/components/NodeDetailPanel.tsx new file mode 100644 index 00000000..182b308a --- /dev/null +++ b/docs/pipeline-diagrams/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/app/components/PipelineDiagram.tsx b/docs/pipeline-diagrams/app/components/PipelineDiagram.tsx new file mode 100644 index 00000000..80c26a03 --- /dev/null +++ b/docs/pipeline-diagrams/app/components/PipelineDiagram.tsx @@ -0,0 +1,649 @@ +"use client"; + +import { useCallback, useEffect, useState, type MouseEvent } from "react"; +import { + ReactFlow, + Background, + Controls, + MiniMap, + ReactFlowProvider, + ViewportPortal, + 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 NODE_WIDTH = 240; +const NODE_HEIGHT = 64; +const NODE_HEIGHT_COMPACT = 58; +const ELK_PORT_SIZE = 0; +const GROUP_PADDING_X = 18; +const GROUP_PADDING_Y = 18; +const GROUP_PADDING_TOP = 24; +const EDGE_LABEL_HEIGHT = 16; +const EDGE_LABEL_MIN_WIDTH = 40; +const EDGE_LABEL_MAX_WIDTH = 180; +const EDGE_LABEL_CHAR_WIDTH = 6; + +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 | string; + label: string; + title: string; + description: string; + nodes: PipelineJsonNode[]; + edges: PipelineJsonEdge[]; + groups?: PipelineJsonGroup[]; +} + +interface PipelineJsonNode { + id: string; + label?: string; + node_type?: string; + description?: string; + details?: string; + source_file?: string; +} + +interface PipelineJsonEdge { + source: string; + target: string; + edge_type?: string; + label?: string; +} + +interface PipelineJsonGroup { + id: string; + label: string; + description?: string; + node_ids: string[]; +} + +interface Point { + x: number; + y: number; +} + +type HandleId = "tt" | "st" | "tr" | "sr" | "tb" | "sb" | "tl" | "sl"; +type PortSide = "NORTH" | "EAST" | "SOUTH" | "WEST"; + +interface ElkRoute { + startPoint?: Point; + endPoint?: Point; + bendPoints?: Point[]; +} + +interface ElkLabel extends Point { + width?: number; + height?: number; + text?: string; +} + +interface ElkLayoutEdge { + id?: string; + sections?: ElkRoute[]; + labels?: ElkLabel[]; +} + +interface NodeSize { + width: number; + height: number; +} + +interface PositionedNode extends NodeSize { + x: number; + y: number; +} + +interface EdgeHandleAssignment { + sourceHandle: HandleId; + targetHandle: HandleId; +} + +interface ElkPortSpec { + id: string; + side: PortSide; +} + +interface EdgePortAssignment extends EdgeHandleAssignment { + sourcePortId: string; + targetPortId: string; + sourceSide: PortSide; + targetSide: PortSide; +} + +interface GroupBox { + id: string; + label: string; + description: string; + x: number; + y: number; + width: number; + height: number; +} + +type PipelineNodeData = Record & { + label: string; + nodeType: string; + description: string; + details: string; + source_file: string; + width: number; + height: number; +}; + +type PipelineEdgeData = Record & { + elkRoute: ElkRoute | null; + elkLabel: ElkLabel | null; +}; + +type PipelineFlowNode = Node; +type PipelineFlowEdge = Edge; + +/** + * Match the fixed dimensions used by PipelineNode so ELK and ReactFlow + * agree on the node boxes the routes attach to. + */ +function estimateNodeSize(node: PipelineJsonNode) { + return { + width: NODE_WIDTH, + height: node.description ? NODE_HEIGHT : NODE_HEIGHT_COMPACT, + }; +} + +function estimateEdgeLabelSize(label: string) { + return { + width: Math.min( + EDGE_LABEL_MAX_WIDTH, + Math.max(EDGE_LABEL_MIN_WIDTH, label.length * EDGE_LABEL_CHAR_WIDTH) + ), + height: EDGE_LABEL_HEIGHT, + }; +} + +function elkPortId(nodeId: string, edgeId: string, role: "source" | "target") { + return `${nodeId}::${edgeId}::${role}`; +} + +function sideForHandle(handleId: HandleId): PortSide { + switch (handleId) { + case "tt": + case "st": + return "NORTH"; + case "tr": + case "sr": + return "EAST"; + case "tb": + case "sb": + return "SOUTH"; + case "tl": + case "sl": + return "WEST"; + } +} + +function elkPortsForNode(ports: ElkPortSpec[]) { + return ports.map((port) => ({ + id: port.id, + width: ELK_PORT_SIZE, + height: ELK_PORT_SIZE, + layoutOptions: { + "org.eclipse.elk.port.side": port.side, + }, + })); +} + +/** + * Assign sourceHandle/targetHandle based on relative node positions. + */ +function assignHandles( + sourceNode: PositionedNode, + targetNode: PositionedNode +): EdgeHandleAssignment { + const sourceCenter = { + x: sourceNode.x + sourceNode.width / 2, + y: sourceNode.y + sourceNode.height / 2, + }; + const targetCenter = { + x: targetNode.x + targetNode.width / 2, + y: targetNode.y + targetNode.height / 2, + }; + const dx = targetCenter.x - sourceCenter.x; + const dy = targetCenter.y - sourceCenter.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" }; +} + +function assignEdgePorts( + edgeId: string, + edge: PipelineJsonEdge, + sourceNode: PositionedNode, + targetNode: PositionedNode +): EdgePortAssignment { + const handles = assignHandles(sourceNode, targetNode); + + return { + ...handles, + sourcePortId: elkPortId(edge.source, edgeId, "source"), + targetPortId: elkPortId(edge.target, edgeId, "target"), + sourceSide: sideForHandle(handles.sourceHandle), + targetSide: sideForHandle(handles.targetHandle), + }; +} + +function getPositionMap( + children: Array<{ id: string; x?: number; y?: number }> | undefined, + nodeSizeMap: Record +) { + const positionMap: Record = {}; + + for (const child of children || []) { + const size = nodeSizeMap[child.id] || { width: NODE_WIDTH, height: NODE_HEIGHT }; + positionMap[child.id] = { + x: child.x || 0, + y: child.y || 0, + width: size.width, + height: size.height, + }; + } + + return positionMap; +} + +function buildNodePortMap( + pipelineEdges: PipelineJsonEdge[], + edgePorts: Record +) { + const nodePortMap: Record = {}; + + pipelineEdges.forEach((edge, i: number) => { + const edgeId = `e-${i}`; + const assignment = edgePorts[edgeId]; + if (!assignment) return; + + nodePortMap[edge.source] = [ + ...(nodePortMap[edge.source] || []), + { id: assignment.sourcePortId, side: assignment.sourceSide }, + ]; + nodePortMap[edge.target] = [ + ...(nodePortMap[edge.target] || []), + { id: assignment.targetPortId, side: assignment.targetSide }, + ]; + }); + + return nodePortMap; +} + +function buildElkGraph( + pipelineNodes: PipelineJsonNode[], + pipelineEdges: PipelineJsonEdge[], + nodeSizeMap: Record, + edgePorts?: Record +) { + const usePorts = Boolean(edgePorts); + const nodePortMap = edgePorts ? buildNodePortMap(pipelineEdges, edgePorts) : {}; + + return { + id: "root", + layoutOptions: ELK_OPTIONS, + children: pipelineNodes.map((n) => { + const size = nodeSizeMap[n.id]; + + return { + id: n.id, + width: size.width, + height: size.height, + ...(usePorts + ? { + layoutOptions: { + "org.eclipse.elk.portConstraints": "FIXED_SIDE", + }, + ports: elkPortsForNode(nodePortMap[n.id] || []), + } + : {}), + }; + }), + edges: pipelineEdges.map((e, i: number) => { + const edgeId = `e-${i}`; + const ports = edgePorts?.[edgeId]; + + return { + id: edgeId, + sources: [ports ? ports.sourcePortId : e.source], + targets: [ports ? ports.targetPortId : e.target], + ...(e.label + ? { + labels: [ + { + text: e.label, + ...estimateEdgeLabelSize(e.label), + }, + ], + } + : {}), + }; + }), + }; +} + +function buildGroupBoxes( + groups: PipelineJsonGroup[] | undefined, + positionMap: Record +): GroupBox[] { + return (groups || []) + .map((group) => { + const groupNodes = group.node_ids + .map((nodeId) => positionMap[nodeId]) + .filter(Boolean) as PositionedNode[]; + + if (groupNodes.length === 0) { + return null; + } + + const minX = Math.min(...groupNodes.map((node) => node.x)); + const minY = Math.min(...groupNodes.map((node) => node.y)); + const maxX = Math.max(...groupNodes.map((node) => node.x + node.width)); + const maxY = Math.max(...groupNodes.map((node) => node.y + node.height)); + + return { + id: group.id, + label: group.label, + description: group.description || "", + x: minX - GROUP_PADDING_X, + y: minY - GROUP_PADDING_TOP, + width: maxX - minX + GROUP_PADDING_X * 2, + height: maxY - minY + GROUP_PADDING_TOP + GROUP_PADDING_Y, + }; + }) + .filter((group): group is GroupBox => group !== null); +} + +async function runElkLayout( + pipelineNodes: PipelineJsonNode[], + pipelineEdges: PipelineJsonEdge[], + pipelineGroups: PipelineJsonGroup[] | undefined +): Promise<{ nodes: PipelineFlowNode[]; edges: PipelineFlowEdge[]; groups: GroupBox[] }> { + const nodeSizeMap: Record = {}; + for (const node of pipelineNodes) { + nodeSizeMap[node.id] = estimateNodeSize(node); + } + + const initialResult = await elk.layout(buildElkGraph(pipelineNodes, pipelineEdges, nodeSizeMap)); + const initialPositionMap = getPositionMap(initialResult.children, nodeSizeMap); + const edgePorts: Record = {}; + + pipelineEdges.forEach((e, i: number) => { + const edgeId = `e-${i}`; + const sourceNode = initialPositionMap[e.source] || { x: 0, y: 0, ...nodeSizeMap[e.source] }; + const targetNode = initialPositionMap[e.target] || { x: 0, y: 0, ...nodeSizeMap[e.target] }; + edgePorts[edgeId] = assignEdgePorts(edgeId, e, sourceNode, targetNode); + }); + + const result = await elk.layout(buildElkGraph(pipelineNodes, pipelineEdges, nodeSizeMap, edgePorts)); + const positionMap = getPositionMap(result.children, nodeSizeMap); + const groups = buildGroupBoxes(pipelineGroups, positionMap); + + // Build edge route map from ELK sections + const routeMap: Record = {}; + const labelMap: Record = {}; + for (const edge of (result.edges || []) as ElkLayoutEdge[]) { + if (edge.id && edge.sections && edge.sections.length > 0) { + routeMap[edge.id] = edge.sections[0]; + } + if (edge.id && edge.labels && edge.labels.length > 0) { + labelMap[edge.id] = edge.labels[0]; + } + } + + // Position nodes + const nodes: PipelineFlowNode[] = pipelineNodes.map((n) => ({ + id: n.id, + type: "pipeline", + position: positionMap[n.id] || { x: 0, y: 0 }, + style: { + width: nodeSizeMap[n.id]?.width || NODE_WIDTH, + height: nodeSizeMap[n.id]?.height || NODE_HEIGHT, + }, + data: { + label: n.label || n.id, + nodeType: n.node_type || "process", + description: n.description || "", + details: n.details || "", + source_file: n.source_file || "", + width: nodeSizeMap[n.id]?.width || NODE_WIDTH, + height: nodeSizeMap[n.id]?.height || NODE_HEIGHT, + }, + })); + + // Enrich edges with handles + ELK routes + const edges: PipelineFlowEdge[] = pipelineEdges.map((e, i: number) => { + const edgeId = `e-${i}`; + const ports = edgePorts[edgeId]; + const route = routeMap[edgeId]; + const edgeStyle = EDGE_STYLES[e.edge_type ?? "data_flow"] || EDGE_STYLES.data_flow; + + return { + id: edgeId, + source: e.source, + target: e.target, + sourceHandle: ports.sourceHandle, + targetHandle: ports.targetHandle, + type: "elk", + label: e.label || undefined, + data: { + elkRoute: route || null, + elkLabel: labelMap[edgeId] || null, + }, + style: { + stroke: edgeStyle.color, + strokeWidth: edgeStyle.width, + strokeDasharray: + edgeStyle.style === "dashed" ? "6 3" : edgeStyle.style === "dotted" ? "2 2" : undefined, + }, + }; + }); + + return { nodes, edges, groups }; +} + +function DiagramInner({ stage }: { stage: StageData }) { + const [nodes, setNodes] = useNodesState([]); + const [edges, setEdges] = useEdgesState([]); + const [groups, setGroups] = useState([]); + const [selectedNode, setSelectedNode] = useState(null); + const { fitView } = useReactFlow(); + + useEffect(() => { + if (!stage?.nodes?.length) return; + + let cancelled = false; + runElkLayout(stage.nodes, stage.edges, stage.groups).then(({ nodes, edges, groups }) => { + if (cancelled) return; + setNodes(nodes); + setEdges(edges); + setGroups(groups); + requestAnimationFrame(() => { + if (cancelled) return; + fitView({ padding: 0.15 }); + }); + }); + + return () => { + cancelled = true; + }; + }, [stage, setNodes, setEdges, fitView]); + + const onNodeClick = useCallback((_: MouseEvent, node: PipelineFlowNode) => { + 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 + +
+
+ + + + {groups.map((group) => ( +
+
+
+ {group.label} +
+ {group.description && ( +
+ {group.description} +
+ )} +
+
+ ))} +
+ + + 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/app/components/PipelineNode.tsx b/docs/pipeline-diagrams/app/components/PipelineNode.tsx new file mode 100644 index 00000000..2e1d7926 --- /dev/null +++ b/docs/pipeline-diagrams/app/components/PipelineNode.tsx @@ -0,0 +1,83 @@ +"use client"; + +import { Handle, Position } from "@xyflow/react"; +import type { CSSProperties } from "react"; +import { NODE_COLORS } from "../colors"; + +const handleClass = "!w-2 !h-2 !border-none"; +const hiddenHandleStyle: CSSProperties = { opacity: 0, pointerEvents: "none" }; + +interface PipelineNodeData { + label: string; + nodeType: string; + description?: string; + width?: number; + height?: number; +} + +/** + * ReactFlow handles are kept for edge bookkeeping. ELK ports own the + * visible attachment geometry, so these handles stay hidden. + * + * 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: CSSProperties = { background: color, ...hiddenHandleStyle }; + return ( + <> + + + + + + + + + + ); +} + +export default function PipelineNode({ data }: { data: PipelineNodeData }) { + 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/app/components/Sidebar.tsx b/docs/pipeline-diagrams/app/components/Sidebar.tsx new file mode 100644 index 00000000..84e7f571 --- /dev/null +++ b/docs/pipeline-diagrams/app/components/Sidebar.tsx @@ -0,0 +1,188 @@ +"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" }, +]; + +const META_STAGES = [ + { label: "Shared build", stageIds: [0, 1, 2] }, + { label: "ECPS pathway (deprecated)", stageIds: ["3a"] }, + { label: "Local area pathway", stageIds: ["3b", 4, 5, 6, 7, 8] }, +]; + +export default function Sidebar() { + const pathname = usePathname(); + const country = "us"; + const stages = pipelineData.stages; + const stageMap = Object.fromEntries(stages.map((s) => [String(s.id), s])); + + return ( + + ); +} diff --git a/docs/pipeline-diagrams/app/favicon.ico b/docs/pipeline-diagrams/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/docs/pipeline-diagrams/app/favicon.ico differ diff --git a/docs/pipeline-diagrams/app/globals.css b/docs/pipeline-diagrams/app/globals.css new file mode 100644 index 00000000..86a0922c --- /dev/null +++ b/docs/pipeline-diagrams/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/app/layout.tsx b/docs/pipeline-diagrams/app/layout.tsx new file mode 100644 index 00000000..e1769cd7 --- /dev/null +++ b/docs/pipeline-diagrams/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/app/page.tsx b/docs/pipeline-diagrams/app/page.tsx new file mode 100644 index 00000000..8da13a42 --- /dev/null +++ b/docs/pipeline-diagrams/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Home() { + redirect("/us"); +} diff --git a/docs/pipeline-diagrams/app/pipeline.json b/docs/pipeline-diagrams/app/pipeline.json new file mode 100644 index 00000000..6df450cc --- /dev/null +++ b/docs/pipeline-diagrams/app/pipeline.json @@ -0,0 +1,2160 @@ +{ + "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" + } + ], + "groups": [] + }, + { + "id": 1, + "label": "Stage 1", + "title": "Stage 1: Base Dataset Construction", + "description": "Build CPS 2024 and PUF 2024 from raw survey data, donor-based labor-market imputations, and retirement contribution inference", + "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_org", + "label": "CPS Basic ORG 2024", + "node_type": "external", + "description": "Monthly CPS basic ORG wage microdata used to train labor-market imputations" + }, + { + "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": "in_cps_pension", + "label": "CPS_2024 / CPS_2021", + "node_type": "input", + "description": "CPS donor sample for pension QRF; falls back to CPS_2021 if current CPS is unavailable" + }, + { + "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_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_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_personal_income_variables", + "label": "Add Income Variables", + "node_type": "process", + "description": "CPS income, transfer, retirement, and QBI-qualification inputs with account-level splits", + "details": "Classifies Social Security by reason code, allocates retirement flows by account type, and adds QBI qualification flags alongside capital-gains splits", + "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_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_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": "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_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": "add_tips", + "label": "Tips Imputation (QRF)", + "node_type": "process", + "description": "Impute tip income and liquid assets from SIPP 2023", + "details": "Cached SIPP models predict tip income plus bank, stock, and bond assets from CPS household context", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_org_inputs", + "label": "ORG Labor-Market Inputs", + "node_type": "process", + "description": "Impute hourly wage, hourly-pay status, and union coverage from CPS Basic ORG donor data", + "details": "Builds an ORG receiver frame from CPS demographics, hours, earnings, and state, then predicts labor-market features with inactivity and self-employment domain constraints", + "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", + "details": "Builds SCF-style reference-person records from CPS household aggregates before QRF prediction", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "add_takeup", + "label": "Benefit Takeup", + "node_type": "us_specific", + "description": "Stochastic takeup and eligibility-alignment draws for major benefit programs", + "details": "Applies rates for EITC, DC PTC, SNAP, ACA, Medicaid, Head Start, Early Head Start, SSI, TANF, and WIC, plus pregnancy and voluntary filing imputations", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "downsample", + "label": "Downsampling", + "node_type": "process", + "description": "Microsimulation.subsample(frac) for standard released CPS vintages", + "details": "Released CPS vintages use frac=0.5; CPS_2024_Full skips this step", + "source_file": "policyengine_us_data/datasets/cps/cps.py" + }, + { + "id": "preprocess_puf", + "label": "Preprocess PUF", + "node_type": "process", + "description": "Rename IRS variables and derive partnership plus Section 199A-ready PolicyEngine inputs", + "details": "Maps core SOI fields, constructs partnership/self-employment components, and adds W-2 wages, UBIA, SSTB, REIT/PTP, and BDC proxies", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "simulate_qbi", + "label": "QBI Simulation", + "node_type": "process", + "description": "Simulate Section 199A W-2 wages and UBIA guardrails from PUF income", + "details": "Uses QBI-source assumptions to generate payroll and property proxies; SSTB and related flags are added later in preprocess_puf", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "impute_puf_demographics", + "label": "Impute PUF Demographics", + "node_type": "process", + "description": "QRF imputation for age, gender, and earnings split", + "details": "Imputes AGEDP1-3, AGERANGE, EARNSPLIT, and GENDER from matched PUF demographic records", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "impute_puf_pension", + "label": "Impute PUF Pension Contributions", + "node_type": "process", + "description": "QRF-impute pre-tax retirement contributions onto PUF tax units from CPS donor records", + "details": "Uses CPS_2024 as the preferred donor dataset, falling back to CPS_2021 during parallel builds; predictors are employment income, decoded age, and gender", + "source_file": "policyengine_us_data/datasets/puf/puf.py" + }, + { + "id": "mortgage_convert", + "label": "Structural Mortgage Conversion", + "node_type": "process", + "description": "Convert deductible mortgage interest into structural mortgage balances, interest, and origination-year inputs", + "details": "Preserves current-law deductible mortgage and total interest deductions while deriving first-lien, secondary acquisition-debt, and non-mortgage residual interest inputs", + "source_file": "policyengine_us_data/utils/mortgage_interest.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_org_inputs", + "edge_type": "data_flow" + }, + { + "source": "in_org", + "target": "add_org_inputs", + "edge_type": "external_source", + "label": "ORG donor data" + }, + { + "source": "add_org_inputs", + "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": "impute_puf_pension", + "edge_type": "data_flow" + }, + { + "source": "in_cps_pension", + "target": "impute_puf_pension", + "edge_type": "data_flow", + "label": "CPS donor sample" + }, + { + "source": "impute_puf_pension", + "target": "mortgage_convert", + "edge_type": "data_flow" + }, + { + "source": "mortgage_convert", + "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_org_inputs", + "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" + }, + { + "source": "util_qrf", + "target": "impute_puf_pension", + "edge_type": "uses_utility" + } + ], + "groups": [] + }, + { + "id": 2, + "label": "Stage 2", + "title": "Stage 2: Extended CPS (PUF Clone)", + "description": "Merge CPS + PUF via cloning, rematch clone features, QRF-impute incomes and CPS-only vars, then finalize Extended CPS inputs", + "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": "in_scf_s2", + "label": "SCF_2022", + "node_type": "input", + "description": "From Stage 0 (mortgage-balance donor sample)" + }, + { + "id": "geo_assign_s2", + "label": "Geography Assignment", + "node_type": "process", + "description": "assign_random_geography() \u2014 population-weighted block draw" + }, + { + "id": "out_ext", + "label": "extended_cps_2024.h5", + "node_type": "output", + "description": "~260K households (doubled), CPS half + PUF half" + }, + { + "id": "util_qrf_s2", + "label": "microimpute QRF", + "node_type": "utility", + "description": "fit_predict() for sequential imputation" + }, + { + "id": "util_knn_s2", + "label": "sklearn NearestNeighbors", + "node_type": "utility", + "description": "Role-aware donor matching on standardized clone predictors" + }, + { + "id": "record_double", + "label": "Record Doubling", + "node_type": "process", + "description": "puf_clone_dataset() \u2014 CPS half keeps originals, PUF half starts with zero weight", + "source_file": "policyengine_us_data/calibration/puf_impute.py" + }, + { + "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", + "source_file": "policyengine_us_data/calibration/puf_impute.py" + }, + { + "id": "retire_impute", + "label": "Retirement Contribution Imputation", + "node_type": "process", + "description": "401k, IRA, SE pension \u2014 IRS limits + catch-up applied", + "source_file": "policyengine_us_data/calibration/puf_impute.py" + }, + { + "id": "weeks_impute", + "label": "Weeks Unemployed Imputation", + "node_type": "process", + "description": "QRF on CPS weeks_unemployed \u2014 clips [0, 52], zero if no UC", + "source_file": "policyengine_us_data/calibration/puf_impute.py" + }, + { + "id": "ss_reconcile", + "label": "SS Sub-component Reconciliation", + "node_type": "process", + "description": "Retirement/Disability/Survivors/Dependents \u2014 scaled to match PUF total", + "source_file": "policyengine_us_data/calibration/puf_impute.py" + }, + { + "id": "clone_features", + "label": "Clone Feature Rematching", + "node_type": "process", + "description": "kNN donor rematch of clone-half sex, race, Hispanic status, and occupation fields", + "details": "Matches within tax-unit roles using demographics plus imputed income, then derives overtime and tipped-occupation inputs from donor occupations when available", + "source_file": "policyengine_us_data/datasets/cps/extended_cps.py" + }, + { + "id": "cps_only", + "label": "CPS-Only Variable Re-imputation", + "node_type": "process", + "description": "Second-stage QRF for CPS-only transfers, SPM, medical, hours, ORG, retirement, and prior-year inputs", + "details": "Trains on CPS persons and predicts clone-half values from demographics plus PUF-imputed income, then applies retirement and ORG domain constraints", + "source_file": "policyengine_us_data/datasets/cps/extended_cps.py" + }, + { + "id": "qrf_pass2", + "label": "QRF Pass 2: Override Imputation", + "node_type": "process", + "description": "Replace the PUF clone half with second-stage CPS-only QRF outputs", + "details": "Keeps original CPS donor values in the first half, maps person-level predictions onto each target entity, and rebuilds capped childcare on the clone half", + "source_file": "policyengine_us_data/datasets/cps/extended_cps.py" + }, + { + "id": "mortgage_hints", + "label": "Mortgage Balance Hint Imputation", + "node_type": "process", + "description": "Impute tax-unit mortgage balance hints from SCF donor balances", + "details": "Fits a weighted QRF on SCF mortgage holders, predicts first-lien and secondary acquisition-debt balance hints, and enforces conservative nonnegative ordering", + "source_file": "policyengine_us_data/utils/mortgage_interest.py" + }, + { + "id": "mortgage_convert", + "label": "Structural Mortgage Conversion", + "node_type": "process", + "description": "Convert deductible mortgage interest into structural mortgage balances, interest, and origination-year inputs", + "details": "Preserves current-law deductible mortgage and total interest deductions while deriving first-lien, secondary acquisition-debt, and non-mortgage residual interest inputs", + "source_file": "policyengine_us_data/utils/mortgage_interest.py" + }, + { + "id": "formula_drop", + "label": "Formula Variable Dropping", + "node_type": "process", + "description": "Rename response inputs, then drop formula, adds, and subtracts variables before save", + "details": "Preserves leaf inputs needed by policyengine-us while keeping stored arrays aligned with the current variable model", + "source_file": "policyengine_us_data/datasets/cps/extended_cps.py" + } + ], + "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": "retire_impute", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass1", + "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": "clone_features", + "edge_type": "data_flow" + }, + { + "source": "clone_features", + "target": "cps_only", + "edge_type": "data_flow" + }, + { + "source": "cps_only", + "target": "qrf_pass2", + "edge_type": "data_flow" + }, + { + "source": "qrf_pass2", + "target": "mortgage_hints", + "edge_type": "data_flow" + }, + { + "source": "in_scf_s2", + "target": "mortgage_hints", + "edge_type": "data_flow", + "label": "SCF donor sample" + }, + { + "source": "mortgage_hints", + "target": "mortgage_convert", + "edge_type": "data_flow" + }, + { + "source": "mortgage_convert", + "target": "formula_drop", + "edge_type": "data_flow" + }, + { + "source": "formula_drop", + "target": "out_ext", + "edge_type": "produces_artifact" + }, + { + "source": "util_qrf_s2", + "target": "qrf_pass1", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "cps_only", + "edge_type": "uses_utility" + }, + { + "source": "util_qrf_s2", + "target": "mortgage_hints", + "edge_type": "uses_utility" + }, + { + "source": "util_knn_s2", + "target": "clone_features", + "edge_type": "uses_utility" + } + ], + "groups": [] + }, + { + "id": "3a", + "label": "Stage 3a", + "title": "Stage 3a: Enhanced CPS Reweighting", + "description": "Reweight Extended CPS to match national IRS/Census/CBO targets, then apply the 2025 ACA post-calibration override (deprecated ECPS pathway)", + "country": "us", + "nodes": [ + { + "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": "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" + }, + { + "id": "reweight", + "label": "reweight()", + "node_type": "process", + "description": "Sparse household-weight calibration against national and state targets", + "details": "Uses Adam with HardConcrete gates; default run is 500 epochs at lr=0.2", + "source_file": "policyengine_us_data/datasets/cps/enhanced_cps.py" + }, + { + "id": "aca_2025_override", + "label": "ACA 2025 Takeup Override", + "node_type": "process", + "description": "Expand 2025 ACA takeup until weighted PTC enrollment matches the post-calibration person target", + "details": "Starts from stored tax-unit takeup, recalculates 2025 aca_ptc enrollment at person level, and flips seeded tax-unit draws until the weighted enrollment target is met", + "source_file": "policyengine_us_data/datasets/cps/enhanced_cps.py" + } + ], + "edges": [ + { + "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": "aca_2025_override", + "edge_type": "data_flow" + }, + { + "source": "aca_2025_override", + "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" + } + ], + "groups": [] + }, + { + "id": "3b", + "label": "Stage 3b", + "title": "Stage 3b: Stratified CPS", + "description": "Stratify Extended CPS by income \u2014 keep top 1%, sample remaining 99%", + "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-8" + } + ], + "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%" + } + ], + "groups": [ + { + "id": "create_stratified", + "label": "create_stratified_cps_dataset()", + "description": "Wrapper around AGI calculation, top-1% retention, and sampling into the stratified CPS artifact", + "node_ids": [ + "calc_agi", + "strat_top", + "strat_sample", + "out_strat" + ] + } + ] + }, + { + "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": "sipp_assets_qrf", + "label": "SIPP Assets QRF", + "node_type": "process", + "description": "5 predictors \u2014 imputes bank_account_assets, stock_assets, bond_assets" + }, + { + "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" + }, + { + "id": "geo_assign_s4", + "label": "Geography Assignment", + "node_type": "process", + "description": "Population-weighted block assignment with AGI-conditioned reweighting for top-income households", + "details": "Preserves within-district block shares while reweighting extreme-household draws to district AGI targets", + "source_file": "policyengine_us_data/calibration/clone_and_assign.py" + }, + { + "id": "acs_qrf", + "label": "ACS QRF Imputation", + "node_type": "process", + "description": "10 predictors incl. state_fips \u2014 imputes rent, real_estate_taxes", + "source_file": "policyengine_us_data/calibration/source_impute.py" + }, + { + "id": "sipp_tips_qrf", + "label": "SIPP Tips QRF", + "node_type": "process", + "description": "4 predictors, NO state \u2014 imputes tip_income", + "source_file": "policyengine_us_data/calibration/source_impute.py" + }, + { + "id": "scf_qrf", + "label": "SCF QRF Imputation", + "node_type": "process", + "description": "8 predictors \u2014 imputes net_worth, auto_loan_balance, auto_loan_interest", + "source_file": "policyengine_us_data/calibration/source_impute.py" + } + ], + "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" + } + ], + "groups": [] + }, + { + "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": "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" + }, + { + "id": "state_precomp", + "label": "Per-State Precomputation", + "node_type": "us_specific", + "description": "51 fresh Microsimulations \u2014 most expensive step", + "source_file": "policyengine_us_data/calibration/unified_matrix_builder.py" + }, + { + "id": "clone_assembly", + "label": "Clone Assembly", + "node_type": "process", + "description": "430 clones \u00d7 12K records = 5.16M columns", + "source_file": "policyengine_us_data/calibration/unified_matrix_builder.py" + } + ], + "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" + } + ], + "groups": [ + { + "id": "run_calibration_build", + "label": "run_calibration()", + "description": "Build phase: resolve targets and constraints, assemble clone values, and package the sparse calibration matrix", + "node_ids": [ + "target_resolve", + "target_uprate", + "geo_build", + "constraint_resolve", + "state_precomp", + "clone_assembly", + "takeup_rerand", + "sparse_build", + "out_pkg" + ] + } + ] + }, + { + "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": "create_model", + "label": "Create SparseCalibrationWeights", + "node_type": "process", + "description": "n_features = 5.16M, init_keep_prob = 0.999" + }, + { + "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" + }, + { + "id": "init_weights", + "label": "Compute Initial Weights", + "node_type": "process", + "description": "Population-proportional per CD", + "source_file": "policyengine_us_data/calibration/unified_calibration.py" + }, + { + "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", + "source_file": "policyengine_us_data/calibration/unified_calibration.py" + } + ], + "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" + } + ], + "groups": [ + { + "id": "run_calibration_fit", + "label": "run_calibration()", + "description": "Fit phase: initialize weights, optimize sparse calibration weights, and emit artifacts plus diagnostics", + "node_ids": [ + "init_weights", + "create_model", + "fit_model", + "extract_weights", + "out_weights", + "out_geo_s6", + "out_diag", + "out_config_s6" + ] + } + ] + }, + { + "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": "worker_s7", + "label": "Modal Worker Container", + "node_type": "external", + "description": "16GB RAM, 1 CPU each, 8-hour timeout" + }, + { + "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" + }, + { + "id": "phase1", + "label": "Phase 1: States", + "node_type": "us_specific", + "description": "51 state H5 files (50 + DC) \u2014 workers in parallel", + "source_file": "policyengine_us_data/calibration/publish_local_area.py" + }, + { + "id": "phase2", + "label": "Phase 2: Districts", + "node_type": "us_specific", + "description": "~435 congressional district H5 files \u2014 workers in parallel", + "source_file": "policyengine_us_data/calibration/publish_local_area.py" + }, + { + "id": "phase3", + "label": "Phase 3: Cities", + "node_type": "us_specific", + "description": "NYC (5 counties, 13 CDs) \u2014 city probability filtering", + "source_file": "policyengine_us_data/calibration/publish_local_area.py" + }, + { + "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", + "source_file": "policyengine_us_data/calibration/publish_local_area.py" + }, + { + "id": "geo_derive", + "label": "derive_geography_from_blocks()", + "node_type": "process", + "description": "15 geographic variables from block GEOID", + "source_file": "policyengine_us_data/calibration/block_assignment.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" + } + ], + "groups": [] + }, + { + "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": "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": "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" + }, + { + "id": "v3", + "label": "Layer 3: Target-Based Validation", + "node_type": "process", + "description": "Full microsimulation against constrained targets, including reform-aware comparisons", + "details": "Caches household and person variables, applies non-geographic constraints, and neutralizes target-specific reforms via income-tax deltas", + "source_file": "policyengine_us_data/calibration/validate_staging.py" + }, + { + "id": "v2", + "label": "Layer 2: Structural Sanity", + "node_type": "process", + "description": "Weight non-negativity, entity ID uniqueness, no NaN/Inf, mapping integrity", + "source_file": "policyengine_us_data/calibration/sanity_checks.py" + }, + { + "id": "staging_upload", + "label": "Upload to Staging", + "node_type": "process", + "description": "upload_to_staging_hf() \u2014 batches of 50 files/commit", + "source_file": "policyengine_us_data/calibration/promote_local_h5s.py" + }, + { + "id": "atomic_promote", + "label": "Atomic Promotion", + "node_type": "process", + "description": "promote_staging_to_production_hf() \u2014 single CommitOperationCopy commit", + "source_file": "policyengine_us_data/calibration/promote_local_h5s.py" + } + ], + "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" + } + ], + "groups": [] + } + ], + "metadata": { + "total_nodes": 167, + "total_edges": 172 + } +} diff --git a/docs/pipeline-diagrams/eslint.config.mjs b/docs/pipeline-diagrams/eslint.config.mjs new file mode 100644 index 00000000..05e726d1 --- /dev/null +++ b/docs/pipeline-diagrams/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/next.config.ts b/docs/pipeline-diagrams/next.config.ts new file mode 100644 index 00000000..e9ffa308 --- /dev/null +++ b/docs/pipeline-diagrams/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/package-lock.json b/docs/pipeline-diagrams/package-lock.json new file mode 100644 index 00000000..dd381ce6 --- /dev/null +++ b/docs/pipeline-diagrams/package-lock.json @@ -0,0 +1,6809 @@ +{ + "name": "pipeline-demos", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pipeline-demos", + "version": "0.1.0", + "dependencies": { + "@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/@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/package.json b/docs/pipeline-diagrams/package.json new file mode 100644 index 00000000..f164ad42 --- /dev/null +++ b/docs/pipeline-diagrams/package.json @@ -0,0 +1,28 @@ +{ + "name": "pipeline-demos", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@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/postcss.config.mjs b/docs/pipeline-diagrams/postcss.config.mjs new file mode 100644 index 00000000..61e36849 --- /dev/null +++ b/docs/pipeline-diagrams/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/docs/pipeline-diagrams/public/file.svg b/docs/pipeline-diagrams/public/file.svg new file mode 100644 index 00000000..004145cd --- /dev/null +++ b/docs/pipeline-diagrams/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/globe.svg b/docs/pipeline-diagrams/public/globe.svg new file mode 100644 index 00000000..567f17b0 --- /dev/null +++ b/docs/pipeline-diagrams/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/next.svg b/docs/pipeline-diagrams/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/docs/pipeline-diagrams/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/vercel.svg b/docs/pipeline-diagrams/public/vercel.svg new file mode 100644 index 00000000..77053960 --- /dev/null +++ b/docs/pipeline-diagrams/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/public/window.svg b/docs/pipeline-diagrams/public/window.svg new file mode 100644 index 00000000..b2b2a44f --- /dev/null +++ b/docs/pipeline-diagrams/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/pipeline-diagrams/tsconfig.json b/docs/pipeline-diagrams/tsconfig.json new file mode 100644 index 00000000..3a13f90a --- /dev/null +++ b/docs/pipeline-diagrams/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/paper/scripts/calculate_distributional_metrics.py b/paper/scripts/calculate_distributional_metrics.py index 61de771b..e4d8f4ea 100644 --- a/paper/scripts/calculate_distributional_metrics.py +++ b/paper/scripts/calculate_distributional_metrics.py @@ -214,7 +214,7 @@ def main(): print(f" Top 10% share: {result['top_10_share']:.3f}") print(f" Top 1% share: {result['top_1_share']:.3f}") else: - print(f" Unable to calculate - using placeholders") + print(" Unable to calculate - using placeholders") # Save results output_dir = Path("paper/results") diff --git a/pipeline_stages.yaml b/pipeline_stages.yaml new file mode 100644 index 00000000..b172f9f1 --- /dev/null +++ b/pipeline_stages.yaml @@ -0,0 +1,829 @@ +# 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, donor-based labor-market imputations, and retirement contribution inference" + 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_org + label: "CPS Basic ORG 2024" + node_type: external + description: "Monthly CPS basic ORG wage microdata used to train labor-market imputations" + - 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: in_cps_pension + label: "CPS_2024 / CPS_2021" + node_type: input + description: "CPS donor sample for pension QRF; falls back to CPS_2021 if current CPS is unavailable" + - 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_org_inputs, edge_type: data_flow } + - { source: in_org, target: add_org_inputs, edge_type: external_source, label: "ORG donor data" } + - { source: add_org_inputs, 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: impute_puf_pension, edge_type: data_flow } + - { source: in_cps_pension, target: impute_puf_pension, edge_type: data_flow, label: "CPS donor sample" } + - { source: impute_puf_pension, target: mortgage_convert, edge_type: data_flow } + - { source: mortgage_convert, 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_org_inputs, 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 } + - { source: util_qrf, target: impute_puf_pension, 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, rematch clone features, QRF-impute incomes and CPS-only vars, then finalize Extended CPS inputs" + 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: in_scf_s2 + label: "SCF_2022" + node_type: input + description: "From Stage 0 (mortgage-balance donor sample)" + - id: geo_assign_s2 + label: "Geography Assignment" + node_type: process + description: "assign_random_geography() — population-weighted block draw" + - id: out_ext + label: "extended_cps_2024.h5" + node_type: output + description: "~260K households (doubled), CPS half + PUF half" + - id: util_qrf_s2 + label: "microimpute QRF" + node_type: utility + description: "fit_predict() for sequential imputation" + - id: util_knn_s2 + label: "sklearn NearestNeighbors" + node_type: utility + description: "Role-aware donor matching on standardized clone predictors" + 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: retire_impute, edge_type: data_flow } + - { source: qrf_pass1, 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: clone_features, edge_type: data_flow } + - { source: clone_features, target: cps_only, edge_type: data_flow } + - { source: cps_only, target: qrf_pass2, edge_type: data_flow } + - { source: qrf_pass2, target: mortgage_hints, edge_type: data_flow } + - { source: in_scf_s2, target: mortgage_hints, edge_type: data_flow, label: "SCF donor sample" } + - { source: mortgage_hints, target: mortgage_convert, edge_type: data_flow } + - { source: mortgage_convert, target: formula_drop, edge_type: data_flow } + - { source: formula_drop, target: out_ext, edge_type: produces_artifact } + - { source: util_qrf_s2, target: qrf_pass1, edge_type: uses_utility } + - { source: util_qrf_s2, target: cps_only, edge_type: uses_utility } + - { source: util_qrf_s2, target: mortgage_hints, edge_type: uses_utility } + - { source: util_knn_s2, target: clone_features, edge_type: uses_utility } + + # ══════════════════════════════════════════════════════════════ + # Stage 3a: Enhanced CPS Reweighting (ECPS — deprecated) + # ══════════════════════════════════════════════════════════════ + - id: "3a" + label: "Stage 3a" + title: "Stage 3a: Enhanced CPS Reweighting" + description: "Reweight Extended CPS to match national IRS/Census/CBO targets, then apply the 2025 ACA post-calibration override (deprecated ECPS pathway)" + country: us + extra_nodes: + - 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: 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_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: aca_2025_override, edge_type: data_flow } + - { source: aca_2025_override, 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 3b: Stratified CPS (Local area pathway) + # ══════════════════════════════════════════════════════════════ + - id: "3b" + label: "Stage 3b" + title: "Stage 3b: Stratified CPS" + description: "Stratify Extended CPS by income — keep top 1%, sample remaining 99%" + country: us + groups: + - id: create_stratified + label: "create_stratified_cps_dataset()" + description: "Wrapper around AGI calculation, top-1% retention, and sampling into the stratified CPS artifact" + node_ids: [calc_agi, strat_top, strat_sample, out_strat] + 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-8" + 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%" } + + # ══════════════════════════════════════════════════════════════ + # 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: sipp_assets_qrf + label: "SIPP Assets QRF" + node_type: process + description: "5 predictors — imputes bank_account_assets, stock_assets, bond_assets" + - 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 + groups: + - id: run_calibration_build + label: "run_calibration()" + description: "Build phase: resolve targets and constraints, assemble clone values, and package the sparse calibration matrix" + node_ids: [target_resolve, target_uprate, geo_build, constraint_resolve, state_precomp, clone_assembly, takeup_rerand, sparse_build, out_pkg] + 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: 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 + groups: + - id: run_calibration_fit + label: "run_calibration()" + description: "Fit phase: initialize weights, optimize sparse calibration weights, and emit artifacts plus diagnostics" + node_ids: [init_weights, create_model, fit_model, extract_weights, out_weights, out_geo_s6, out_diag, out_config_s6] + 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: create_model + label: "Create SparseCalibrationWeights" + node_type: process + description: "n_features = 5.16M, init_keep_prob = 0.999" + - 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: worker_s7 + label: "Modal Worker Container" + node_type: external + description: "16GB RAM, 1 CPU each, 8-hour timeout" + - 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: 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: 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/__version__.py b/policyengine_us_data/__version__.py index 1c893e3c..6224382a 100644 --- a/policyengine_us_data/__version__.py +++ b/policyengine_us_data/__version__.py @@ -10,5 +10,5 @@ with open("pyproject.toml", "rb") as f: pyproject = tomli.load(f) __version__ = pyproject["project"]["version"] -except Exception as e: +except Exception: __version__ = importlib.metadata.version("policyengine_us_data") diff --git a/policyengine_us_data/calibration/block_assignment.py b/policyengine_us_data/calibration/block_assignment.py index 3754ad5a..597fa341 100644 --- a/policyengine_us_data/calibration/block_assignment.py +++ b/policyengine_us_data/calibration/block_assignment.py @@ -35,6 +35,8 @@ County, ) from policyengine_us_data.storage import STORAGE_FOLDER +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode # === GEOID Parsing Functions === # Block GEOID format: SSCCCTTTTTTBBBB (15 chars) @@ -510,6 +512,15 @@ def assign_geography_for_cd( } +@pipeline_node( + PipelineNode( + id="geo_derive", + label="derive_geography_from_blocks()", + node_type="process", + description="15 geographic variables from block GEOID", + source_file="policyengine_us_data/calibration/block_assignment.py", + ) +) def derive_geography_from_blocks( block_geoids: np.ndarray, ) -> Dict[str, np.ndarray]: diff --git a/policyengine_us_data/calibration/clone_and_assign.py b/policyengine_us_data/calibration/clone_and_assign.py index defcea17..40c17808 100644 --- a/policyengine_us_data/calibration/clone_and_assign.py +++ b/policyengine_us_data/calibration/clone_and_assign.py @@ -8,6 +8,8 @@ import pandas as pd from policyengine_us_data.storage import STORAGE_FOLDER +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode logger = logging.getLogger(__name__) @@ -93,6 +95,16 @@ def _build_agi_block_probs(cds, pop_probs, cd_agi_targets): return agi_probs / agi_probs.sum() +@pipeline_node( + PipelineNode( + id="geo_assign_s4", + label="Geography Assignment", + node_type="process", + description="Population-weighted block assignment with AGI-conditioned reweighting for top-income households", + details="Preserves within-district block shares while reweighting extreme-household draws to district AGI targets", + source_file="policyengine_us_data/calibration/clone_and_assign.py", + ) +) def assign_random_geography( n_records: int, n_clones: int = 10, diff --git a/policyengine_us_data/calibration/create_stratified_cps.py b/policyengine_us_data/calibration/create_stratified_cps.py index 2aa15a9f..3e9aa831 100644 --- a/policyengine_us_data/calibration/create_stratified_cps.py +++ b/policyengine_us_data/calibration/create_stratified_cps.py @@ -13,8 +13,19 @@ from policyengine_us import Microsimulation from policyengine_core.data.dataset import Dataset from policyengine_core.enums import Enum +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode +@pipeline_node( + PipelineNode( + id="create_stratified", + label="Stratified CPS Dataset", + node_type="process", + description="AGI calculation, top 1% retention, uniform sample of remaining 99%", + source_file="policyengine_us_data/calibration/create_stratified_cps.py", + ) +) def create_stratified_cps_dataset( target_households=30_000, high_income_percentile=99, @@ -74,7 +85,7 @@ def create_stratified_cps_dataset( n_bottom_25 = np.sum(agi < bottom_25_pct_threshold) n_middle = n_households_orig - n_top - n_bottom_25 - print(f"\nStratum sizes:") + print("\nStratum sizes:") print( f" Top {100 - high_income_percentile}% (AGI >= ${high_income_threshold:,.0f}): {n_top:,}" ) @@ -103,7 +114,7 @@ def create_stratified_cps_dataset( r_middle = min(1.0, r_middle) r_bottom = min(1.0, r_bottom) - print(f"\nSampling rates:") + print("\nSampling rates:") print(f" Top {100 - high_income_percentile}%: 100%") print(f" Middle 25-{high_income_percentile}%: {r_middle:.1%}") print(f" Bottom 25%: {r_bottom:.1%}") @@ -114,7 +125,7 @@ def create_stratified_cps_dataset( expected_bottom = int(n_bottom_25 * r_bottom) expected_total = expected_top + expected_middle + expected_bottom - print(f"\nExpected selection:") + print("\nExpected selection:") print(f" Top {100 - high_income_percentile}%: {expected_top:,}") print(f" Middle 25-{high_income_percentile}%: {expected_middle:,}") print(f" Bottom 25%: {expected_bottom:,}") @@ -256,7 +267,7 @@ def create_stratified_cps_dataset( for period, values in periods.items(): grp.create_dataset(str(period), data=values) - print(f"Stratified CPS dataset saved successfully!") + print("Stratified CPS dataset saved successfully!") # Verify the saved file print("\nVerifying saved file...") @@ -285,7 +296,7 @@ def create_stratified_cps_dataset( max_agi_original = np.max(agi) max_agi_stratified = np.max(agi_stratified) - print(f"\nMaximum AGI:") + print("\nMaximum AGI:") print(f" Original: ${max_agi_original:,.0f}") print(f" Stratified: ${max_agi_stratified:,.0f}") @@ -315,7 +326,7 @@ def create_stratified_cps_dataset( elif arg.isdigit(): target = int(arg) - print(f"Creating stratified dataset:") + print("Creating stratified dataset:") print(f" Target households: {target:,}") print(f" Keep all above: {high_pct}th percentile") print(f" Oversample poor: {oversample}") diff --git a/policyengine_us_data/calibration/promote_local_h5s.py b/policyengine_us_data/calibration/promote_local_h5s.py index ccefb546..305c46c6 100644 --- a/policyengine_us_data/calibration/promote_local_h5s.py +++ b/policyengine_us_data/calibration/promote_local_h5s.py @@ -30,6 +30,8 @@ upload_from_hf_staging_to_gcs, cleanup_staging_hf, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode logger = logging.getLogger(__name__) @@ -48,12 +50,30 @@ def collect_files(local_dir: Path, area_types: list) -> list: return files +@pipeline_node( + PipelineNode( + id="staging_upload", + label="Upload to Staging", + node_type="process", + description="upload_to_staging_hf() — batches of 50 files/commit", + source_file="policyengine_us_data/calibration/promote_local_h5s.py", + ) +) def stage(files: list, version: str, run_id: str = ""): logger.info("Uploading %d files to HF staging/...", len(files)) n = upload_to_staging_hf(files, version=version, run_id=run_id) logger.info("Staged %d files", n) +@pipeline_node( + PipelineNode( + id="atomic_promote", + label="Atomic Promotion", + node_type="process", + description="promote_staging_to_production_hf() — single CommitOperationCopy commit", + source_file="policyengine_us_data/calibration/promote_local_h5s.py", + ) +) def promote(rel_paths: list, version: str, run_id: str = ""): logger.info( "Promoting %d files from staging/ to production...", diff --git a/policyengine_us_data/calibration/publish_local_area.py b/policyengine_us_data/calibration/publish_local_area.py index 2a017668..3bda1a80 100644 --- a/policyengine_us_data/calibration/publish_local_area.py +++ b/policyengine_us_data/calibration/publish_local_area.py @@ -40,6 +40,8 @@ apply_block_takeup_to_arrays, reported_subsidized_marketplace_by_tax_unit, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode CHECKPOINT_FILE = Path("completed_states.txt") CHECKPOINT_FILE_DISTRICTS = Path("completed_districts.txt") @@ -186,6 +188,15 @@ def _build_reported_takeup_anchors( return reported_anchors +@pipeline_node( + PipelineNode( + 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", + source_file="policyengine_us_data/calibration/publish_local_area.py", + ) +) def build_h5( weights: np.ndarray, geography, @@ -637,6 +648,15 @@ def get_district_friendly_name(cd_geoid: str) -> str: return f"{state_code}-{district_num:02d}" +@pipeline_node( + PipelineNode( + id="phase1", + label="Phase 1: States", + node_type="us_specific", + description="51 state H5 files (50 + DC) — workers in parallel", + source_file="policyengine_us_data/calibration/publish_local_area.py", + ) +) def build_states( weights_path: Path, dataset_path: Path, @@ -704,6 +724,15 @@ def build_states( upload_local_area_batch_to_hf(hf_queue) +@pipeline_node( + PipelineNode( + id="phase2", + label="Phase 2: Districts", + node_type="us_specific", + description="~435 congressional district H5 files — workers in parallel", + source_file="policyengine_us_data/calibration/publish_local_area.py", + ) +) def build_districts( weights_path: Path, dataset_path: Path, @@ -772,6 +801,15 @@ def build_districts( upload_local_area_batch_to_hf(hf_queue) +@pipeline_node( + PipelineNode( + id="phase3", + label="Phase 3: Cities", + node_type="us_specific", + description="NYC (5 counties, 13 CDs) — city probability filtering", + source_file="policyengine_us_data/calibration/publish_local_area.py", + ) +) def build_cities( weights_path: Path, dataset_path: Path, diff --git a/policyengine_us_data/calibration/puf_impute.py b/policyengine_us_data/calibration/puf_impute.py index b87f846f..e219c54a 100644 --- a/policyengine_us_data/calibration/puf_impute.py +++ b/policyengine_us_data/calibration/puf_impute.py @@ -19,6 +19,8 @@ import pandas as pd import yaml +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode from policyengine_us_data.utils.retirement_limits import ( get_retirement_limits, ) @@ -373,6 +375,15 @@ def _age_heuristic_ss_shares( return shares +@pipeline_node( + PipelineNode( + id="ss_reconcile", + label="SS Sub-component Reconciliation", + node_type="process", + description="Retirement/Disability/Survivors/Dependents — scaled to match PUF total", + source_file="policyengine_us_data/calibration/puf_impute.py", + ) +) def reconcile_ss_subcomponents( data: Dict[str, Dict[int, np.ndarray]], n_cps: int, @@ -425,6 +436,15 @@ def reconcile_ss_subcomponents( data[sub][time_period] = arr +@pipeline_node( + PipelineNode( + id="record_double", + label="Record Doubling", + node_type="process", + description="puf_clone_dataset() — CPS half keeps originals, PUF half starts with zero weight", + source_file="policyengine_us_data/calibration/puf_impute.py", + ) +) def puf_clone_dataset( data: Dict[str, Dict[int, np.ndarray]], state_fips: np.ndarray, @@ -552,6 +572,15 @@ def _map_to_entity(pred_values, variable_name): return new_data +@pipeline_node( + PipelineNode( + id="weeks_impute", + label="Weeks Unemployed Imputation", + node_type="process", + description="QRF on CPS weeks_unemployed — clips [0, 52], zero if no UC", + source_file="policyengine_us_data/calibration/puf_impute.py", + ) +) def _impute_weeks_unemployed( data: Dict[str, Dict[int, np.ndarray]], puf_imputations: Dict[str, np.ndarray], @@ -644,6 +673,15 @@ def _impute_weeks_unemployed( return imputed_weeks +@pipeline_node( + PipelineNode( + id="retire_impute", + label="Retirement Contribution Imputation", + node_type="process", + description="401k, IRA, SE pension — IRS limits + catch-up applied", + source_file="policyengine_us_data/calibration/puf_impute.py", + ) +) def _impute_retirement_contributions( data: Dict[str, Dict[int, np.ndarray]], puf_imputations: Dict[str, np.ndarray], @@ -770,6 +808,15 @@ def _impute_retirement_contributions( return result +@pipeline_node( + PipelineNode( + id="qrf_pass1", + label="QRF Pass 1: Full Imputation", + node_type="process", + description="64 income variables — training on PUF ~20K records, 7 demographic predictors", + source_file="policyengine_us_data/calibration/puf_impute.py", + ) +) def _run_qrf_imputation( data: Dict[str, Dict[int, np.ndarray]], time_period: int, diff --git a/policyengine_us_data/calibration/sanity_checks.py b/policyengine_us_data/calibration/sanity_checks.py index 025d3de8..46d016d2 100644 --- a/policyengine_us_data/calibration/sanity_checks.py +++ b/policyengine_us_data/calibration/sanity_checks.py @@ -12,6 +12,9 @@ import h5py import numpy as np +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode + logger = logging.getLogger(__name__) KEY_MONETARY_VARS = [ @@ -36,6 +39,15 @@ ] +@pipeline_node( + PipelineNode( + id="v2", + label="Layer 2: Structural Sanity", + node_type="process", + description="Weight non-negativity, entity ID uniqueness, no NaN/Inf, mapping integrity", + source_file="policyengine_us_data/calibration/sanity_checks.py", + ) +) def run_sanity_checks( h5_path: str, period: int = 2024, diff --git a/policyengine_us_data/calibration/source_impute.py b/policyengine_us_data/calibration/source_impute.py index ff0de922..0ba0b829 100644 --- a/policyengine_us_data/calibration/source_impute.py +++ b/policyengine_us_data/calibration/source_impute.py @@ -39,6 +39,8 @@ build_org_receiver_frame, predict_org_features, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode logger = logging.getLogger(__name__) @@ -265,6 +267,15 @@ def _person_state_fips( return np.repeat(state_fips, counts) +@pipeline_node( + PipelineNode( + id="acs_qrf", + label="ACS QRF Imputation", + node_type="process", + description="10 predictors incl. state_fips — imputes rent, real_estate_taxes", + source_file="policyengine_us_data/calibration/source_impute.py", + ) +) def _impute_acs( data: Dict[str, Dict[int, np.ndarray]], state_fips: np.ndarray, @@ -349,6 +360,15 @@ def _impute_acs( return data +@pipeline_node( + PipelineNode( + id="sipp_tips_qrf", + label="SIPP Tips QRF", + node_type="process", + description="4 predictors, NO state — imputes tip_income", + source_file="policyengine_us_data/calibration/source_impute.py", + ) +) def _impute_sipp( data: Dict[str, Dict[int, np.ndarray]], state_fips: np.ndarray, @@ -628,6 +648,15 @@ def _impute_sipp( return data +@pipeline_node( + PipelineNode( + id="scf_qrf", + label="SCF QRF Imputation", + node_type="process", + description="8 predictors — imputes net_worth, auto_loan_balance, auto_loan_interest", + source_file="policyengine_us_data/calibration/source_impute.py", + ) +) def _impute_scf( data: Dict[str, Dict[int, np.ndarray]], state_fips: np.ndarray, diff --git a/policyengine_us_data/calibration/unified_calibration.py b/policyengine_us_data/calibration/unified_calibration.py index 1bb09ce0..a1c55341 100644 --- a/policyengine_us_data/calibration/unified_calibration.py +++ b/policyengine_us_data/calibration/unified_calibration.py @@ -35,6 +35,9 @@ import numpy as np +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode + logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", @@ -472,6 +475,15 @@ def load_calibration_package(path: str) -> dict: return package +@pipeline_node( + PipelineNode( + id="init_weights", + label="Compute Initial Weights", + node_type="process", + description="Population-proportional per CD", + source_file="policyengine_us_data/calibration/unified_calibration.py", + ) +) def compute_initial_weights( X_sparse, targets_df: "pd.DataFrame", @@ -534,6 +546,15 @@ def compute_initial_weights( return initial_weights +@pipeline_node( + PipelineNode( + id="fit_model", + label="model.fit()", + node_type="process", + description="Adam optimizer — loss = RSE + λ₀·L0(w) + λ₂·‖w‖²", + source_file="policyengine_us_data/calibration/unified_calibration.py", + ) +) def fit_l0_weights( X_sparse, targets: np.ndarray, @@ -800,6 +821,15 @@ def compute_diagnostics( ) +@pipeline_node( + PipelineNode( + id="run_calibration", + label="Run Calibration Pipeline", + node_type="process", + description="End-to-end L0 calibration: build matrix → fit weights → diagnostics", + source_file="policyengine_us_data/calibration/unified_calibration.py", + ) +) def run_calibration( dataset_path: str, db_path: str, diff --git a/policyengine_us_data/calibration/unified_matrix_builder.py b/policyengine_us_data/calibration/unified_matrix_builder.py index 53408fea..7fc210fe 100644 --- a/policyengine_us_data/calibration/unified_matrix_builder.py +++ b/policyengine_us_data/calibration/unified_matrix_builder.py @@ -28,6 +28,8 @@ apply_op, get_geo_level, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode logger = logging.getLogger(__name__) @@ -95,6 +97,15 @@ def _compute_reform_household_values( return reform_hh +@pipeline_node( + PipelineNode( + id="state_precomp", + label="Per-State Precomputation", + node_type="us_specific", + description="51 fresh Microsimulations — most expensive step", + source_file="policyengine_us_data/calibration/unified_matrix_builder.py", + ) +) def _compute_single_state( dataset_path: str, time_period: int, @@ -415,6 +426,15 @@ def _init_clone_worker(shared_data: dict) -> None: _CLONE_SHARED.update(shared_data) +@pipeline_node( + PipelineNode( + id="clone_assembly", + label="Clone Assembly", + node_type="process", + description="430 clones × 12K records = 5.16M columns", + source_file="policyengine_us_data/calibration/unified_matrix_builder.py", + ) +) def _assemble_clone_values_standalone( state_values: dict, clone_states: np.ndarray, diff --git a/policyengine_us_data/calibration/validate_staging.py b/policyengine_us_data/calibration/validate_staging.py index 1862fbbd..3ea329fc 100644 --- a/policyengine_us_data/calibration/validate_staging.py +++ b/policyengine_us_data/calibration/validate_staging.py @@ -42,6 +42,8 @@ run_sanity_checks, ) from policyengine_us_data.db.create_database_tables import create_or_replace_views +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode logger = logging.getLogger(__name__) @@ -296,6 +298,16 @@ def _get_reform_income_tax_delta( return reform_delta_cache[variable] +@pipeline_node( + PipelineNode( + id="v3", + label="Layer 3: Target-Based Validation", + node_type="process", + description="Full microsimulation against constrained targets, including reform-aware comparisons", + details="Caches household and person variables, applies non-geographic constraints, and neutralizes target-specific reforms via income-tax deltas", + source_file="policyengine_us_data/calibration/validate_staging.py", + ) +) def validate_area( sim, targets_df: pd.DataFrame, diff --git a/policyengine_us_data/datasets/cps/cps.py b/policyengine_us_data/datasets/cps/cps.py index e227760a..1c614304 100644 --- a/policyengine_us_data/datasets/cps/cps.py +++ b/policyengine_us_data/datasets/cps/cps.py @@ -34,6 +34,8 @@ assign_takeup_with_reported_anchors, reported_subsidized_marketplace_by_tax_unit, ) +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode CURRENT_HEALTH_COVERAGE_REPORTED_VAR_MAP = { @@ -156,6 +158,16 @@ 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="Microsimulation.subsample(frac) for standard released CPS vintages", + details="Released CPS vintages use frac=0.5; CPS_2024_Full skips this step", + source_file="policyengine_us_data/datasets/cps/cps.py", + ) + ) def downsample(self, frac: float): from policyengine_us import Microsimulation @@ -171,6 +183,16 @@ def downsample(self, frac: float): ) +@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( { @@ -243,6 +265,16 @@ 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 and eligibility-alignment draws for major benefit programs", + details="Applies rates for EITC, DC PTC, SNAP, ACA, Medicaid, Head Start, Early Head Start, SSI, TANF, and WIC, plus pregnancy and voluntary filing imputations", + source_file="policyengine_us_data/datasets/cps/cps.py", + ) +) def add_takeup(self): data = self.load_dataset() @@ -414,6 +446,15 @@ 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, @@ -463,6 +504,16 @@ 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. @@ -575,6 +626,16 @@ 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="CPS income, transfer, retirement, and QBI-qualification inputs with account-level splits", + details="Classifies Social Security by reason code, allocates retirement flows by account type, and adds QBI qualification flags alongside capital-gains splits", + source_file="policyengine_us_data/datasets/cps/cps.py", + ) +) def add_personal_income_variables(cps: h5py.File, person: DataFrame, year: int): """Add income variables. @@ -832,6 +893,15 @@ 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, @@ -881,6 +951,15 @@ 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 @@ -904,6 +983,15 @@ 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( @@ -964,6 +1052,16 @@ 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, @@ -1713,7 +1811,7 @@ def get_arrival_year_midpoint(peinusyr): cps["ssn_card_type"] = ssn_card_type_str # Final population summary - print(f"\nFinal populations:") + print("\nFinal populations:") for code, label in code_to_str.items(): pop = np.sum(person_weights[ssn_card_type == code]) print(f" Code {code} ({label}): {pop:,.0f}") @@ -1846,6 +1944,16 @@ 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="Cached SIPP models predict tip income plus bank, stock, and bond assets from CPS household context", + 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 @@ -1927,6 +2035,16 @@ def add_tips(self, cps: h5py.File): self.save_dataset(cps) +@pipeline_node( + PipelineNode( + id="add_org_inputs", + label="ORG Labor-Market Inputs", + node_type="process", + description="Impute hourly wage, hourly-pay status, and union coverage from CPS Basic ORG donor data", + details="Builds an ORG receiver frame from CPS demographics, hours, earnings, and state, then predicts labor-market features with inactivity and self-employment domain constraints", + source_file="policyengine_us_data/datasets/cps/cps.py", + ) +) def add_org_labor_market_inputs(cps: h5py.File) -> None: """Impute ORG-derived wage and union inputs onto CPS persons.""" n_persons = len(np.asarray(cps["age"])) @@ -2030,6 +2148,16 @@ 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", + details="Builds SCF-style reference-person records from CPS household aggregates before QRF prediction", + 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/cps/enhanced_cps.py b/policyengine_us_data/datasets/cps/enhanced_cps.py index 3d691ff4..c023be6d 100644 --- a/policyengine_us_data/datasets/cps/enhanced_cps.py +++ b/policyengine_us_data/datasets/cps/enhanced_cps.py @@ -21,6 +21,8 @@ extend_aca_takeup_to_match_target, ) import logging +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode try: import torch @@ -68,6 +70,16 @@ def _set_period_array( period_values[period] = values +@pipeline_node( + PipelineNode( + id="aca_2025_override", + label="ACA 2025 Takeup Override", + node_type="process", + description="Expand 2025 ACA takeup until weighted PTC enrollment matches the post-calibration person target", + details="Starts from stored tax-unit takeup, recalculates 2025 aca_ptc enrollment at person level, and flips seeded tax-unit draws until the weighted enrollment target is met", + source_file="policyengine_us_data/datasets/cps/enhanced_cps.py", + ) +) def create_aca_2025_takeup_override( base_takeup: np.ndarray, person_enrolled_if_takeup: np.ndarray, @@ -100,6 +112,16 @@ def create_aca_2025_takeup_override( ) +@pipeline_node( + PipelineNode( + id="reweight", + label="reweight()", + node_type="process", + description="Sparse household-weight calibration against national and state targets", + details="Uses Adam with HardConcrete gates; default run is 500 epochs at lr=0.2", + source_file="policyengine_us_data/datasets/cps/enhanced_cps.py", + ) +) def reweight( original_weights, loss_matrix, diff --git a/policyengine_us_data/datasets/cps/extended_cps.py b/policyengine_us_data/datasets/cps/extended_cps.py index 8e01f505..a2fe4261 100644 --- a/policyengine_us_data/datasets/cps/extended_cps.py +++ b/policyengine_us_data/datasets/cps/extended_cps.py @@ -19,6 +19,8 @@ impute_tax_unit_mortgage_balance_hints, ) from policyengine_us_data.utils.policyengine import has_policyengine_us_variables +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode from policyengine_us_data.utils.retirement_limits import ( get_retirement_limits, get_se_pension_limits, @@ -349,6 +351,16 @@ def _impute_clone_cps_features( return predictions +@pipeline_node( + PipelineNode( + id="clone_features", + label="Clone Feature Rematching", + node_type="process", + description="kNN donor rematch of clone-half sex, race, Hispanic status, and occupation fields", + details="Matches within tax-unit roles using demographics plus imputed income, then derives overtime and tipped-occupation inputs from donor occupations when available", + source_file="policyengine_us_data/datasets/cps/extended_cps.py", + ) +) def _splice_clone_feature_predictions( data: dict, predictions: pd.DataFrame, @@ -371,6 +383,16 @@ def _splice_clone_feature_predictions( return data +@pipeline_node( + PipelineNode( + id="cps_only", + label="CPS-Only Variable Re-imputation", + node_type="process", + description="Second-stage QRF for CPS-only transfers, SPM, medical, hours, ORG, retirement, and prior-year inputs", + details="Trains on CPS persons and predicts clone-half values from demographics plus PUF-imputed income, then applies retirement and ORG domain constraints", + source_file="policyengine_us_data/datasets/cps/extended_cps.py", + ) +) def _impute_cps_only_variables( data: dict, time_period: int, @@ -741,6 +763,16 @@ def _apply_post_processing(predictions, X_test, time_period, data): return predictions +@pipeline_node( + PipelineNode( + id="qrf_pass2", + label="QRF Pass 2: Override Imputation", + node_type="process", + description="Replace the PUF clone half with second-stage CPS-only QRF outputs", + details="Keeps original CPS donor values in the first half, maps person-level predictions onto each target entity, and rebuilds capped childcare on the clone half", + source_file="policyengine_us_data/datasets/cps/extended_cps.py", + ) +) def _splice_cps_only_predictions( data: dict, predictions: pd.DataFrame, @@ -952,6 +984,16 @@ def _keep_formula_vars(cls): "tax_exempt_pension_income": "tax_exempt_private_pension_income", } + @pipeline_node( + PipelineNode( + id="formula_drop", + label="Formula Variable Dropping", + node_type="process", + description="Rename response inputs, then drop formula, adds, and subtracts variables before save", + details="Preserves leaf inputs needed by policyengine-us while keeping stored arrays aligned with the current variable model", + source_file="policyengine_us_data/datasets/cps/extended_cps.py", + ) + ) @classmethod def _drop_formula_variables(cls, data): """Remove variables that are computed by policyengine-us. diff --git a/policyengine_us_data/datasets/cps/long_term/build_long_term_target_sources.py b/policyengine_us_data/datasets/cps/long_term/build_long_term_target_sources.py index 20655c35..e359414f 100644 --- a/policyengine_us_data/datasets/cps/long_term/build_long_term_target_sources.py +++ b/policyengine_us_data/datasets/cps/long_term/build_long_term_target_sources.py @@ -2,7 +2,6 @@ import argparse import json -from pathlib import Path import pandas as pd from policyengine_us_data.storage import STORAGE_FOLDER diff --git a/policyengine_us_data/datasets/cps/long_term/evaluate_support_augmentation.py b/policyengine_us_data/datasets/cps/long_term/evaluate_support_augmentation.py index a90c2338..275fb111 100644 --- a/policyengine_us_data/datasets/cps/long_term/evaluate_support_augmentation.py +++ b/policyengine_us_data/datasets/cps/long_term/evaluate_support_augmentation.py @@ -2,7 +2,6 @@ import argparse import json -from pathlib import Path import numpy as np from policyengine_us import Microsimulation diff --git a/policyengine_us_data/datasets/cps/long_term/run_household_projection.py b/policyengine_us_data/datasets/cps/long_term/run_household_projection.py index 4d07e4d8..afb1e4c2 100644 --- a/policyengine_us_data/datasets/cps/long_term/run_household_projection.py +++ b/policyengine_us_data/datasets/cps/long_term/run_household_projection.py @@ -542,10 +542,10 @@ def _compose_reforms(*reforms): print("=" * 70) print(f"HOUSEHOLD-LEVEL INCOME TAX PROJECTION: {START_YEAR}-{END_YEAR}") print("=" * 70) -print(f"\nConfiguration:") +print("\nConfiguration:") print(f" Base year: {BASE_YEAR} (CPS microdata)") print(f" Projection: {START_YEAR}-{END_YEAR}") -print(f" Calculation level: HOUSEHOLD ONLY (simplified)") +print(" Calculation level: HOUSEHOLD ONLY (simplified)") print(f" Calibration profile: {PROFILE.name}") print(f" Profile description: {PROFILE.description}") print(f" Target source: {TARGET_SOURCE}") @@ -562,20 +562,20 @@ def _compose_reforms(*reforms): f"{SUPPORT_AUGMENTATION_BLUEPRINT_BASE_WEIGHT_SCALE}" ) if USE_SS: - print(f" Including Social Security benefits constraint: Yes") + print(" Including Social Security benefits constraint: Yes") if USE_PAYROLL: - print(f" Including taxable payroll constraint: Yes") + print(" Including taxable payroll constraint: Yes") if USE_H6_REFORM: - print(f" Including H6 reform income impact constraint: Yes") + print(" Including H6 reform income impact constraint: Yes") if USE_TOB: - print(f" Including TOB revenue constraint: Yes") + print(" Including TOB revenue constraint: Yes") elif BENCHMARK_TOB: - print(f" Benchmarking TOB after calibration: Yes") + print(" Benchmarking TOB after calibration: Yes") if SAVE_H5: print(f" Saving year-specific .h5 files: Yes (to {OUTPUT_DIR}/)") os.makedirs(OUTPUT_DIR, exist_ok=True) else: - print(f" Saving year-specific .h5 files: No (use --save-h5 to enable)") + print(" Saving year-specific .h5 files: No (use --save-h5 to enable)") print(f" Years to process: {END_YEAR - START_YEAR + 1}") est_time = (END_YEAR - START_YEAR + 1) * (3 if SAVE_H5 else 2) print(f" Estimated time: ~{est_time:.0f} minutes") @@ -705,7 +705,7 @@ def _print_support_augmentation_summary(augmentation_report: dict) -> None: n_ages = target_matrix.shape[0] print(f"\nLoaded SSA projections: {n_ages} ages x {n_years} years") -print(f"\nPopulation projections:") +print("\nPopulation projections:") display_years = [ y diff --git a/policyengine_us_data/datasets/puf/puf.py b/policyengine_us_data/datasets/puf/puf.py index bde0f33f..535c05b0 100644 --- a/policyengine_us_data/datasets/puf/puf.py +++ b/policyengine_us_data/datasets/puf/puf.py @@ -20,6 +20,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) @@ -57,6 +59,16 @@ def conditionally_sample_lognormal(flag, target_mean, log_sigma, rng): ) +@pipeline_node( + PipelineNode( + id="simulate_qbi", + label="QBI Simulation", + node_type="process", + description="Simulate Section 199A W-2 wages and UBIA guardrails from PUF income", + details="Uses QBI-source assumptions to generate payroll and property proxies; SSTB and related flags are added later in preprocess_puf", + 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 @@ -163,6 +175,16 @@ def simulate_w2_and_ubia_from_puf(puf, *, seed=None, diagnostics=True): return w2_wages, ubia +@pipeline_node( + PipelineNode( + id="impute_puf_pension", + label="Impute PUF Pension Contributions", + node_type="process", + description="QRF-impute pre-tax retirement contributions onto PUF tax units from CPS donor records", + details="Uses CPS_2024 as the preferred donor dataset, falling back to CPS_2021 during parallel builds; predictors are employment income, decoded age, and gender", + source_file="policyengine_us_data/datasets/puf/puf.py", + ) +) def impute_pension_contributions_to_puf(puf_df): from policyengine_us import Microsimulation from policyengine_us_data.datasets.cps import CPS_2024 @@ -206,6 +228,16 @@ 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", + details="Imputes AGEDP1-3, AGERANGE, EARNSPLIT, and GENDER from matched PUF demographic records", + source_file="policyengine_us_data/datasets/puf/puf.py", + ) +) def impute_missing_demographics( puf: pd.DataFrame, demographics: pd.DataFrame ) -> pd.DataFrame: @@ -313,6 +345,16 @@ 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 IRS variables and derive partnership plus Section 199A-ready PolicyEngine inputs", + details="Maps core SOI fields, constructs partnership/self-employment components, and adds W-2 wages, UBIA, SSTB, REIT/PTP, and BDC proxies", + 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/datasets/scf/fed_scf.py b/policyengine_us_data/datasets/scf/fed_scf.py index 8d2238d1..8209e8fe 100644 --- a/policyengine_us_data/datasets/scf/fed_scf.py +++ b/policyengine_us_data/datasets/scf/fed_scf.py @@ -31,7 +31,7 @@ def generate(self): def _load_unlocked(self) -> pd.DataFrame: # Check if file exists if not os.path.exists(self.file_path): - print(f"Raw SCF dataset file not found. Generating it.") + print("Raw SCF dataset file not found. Generating it.") self._generate_unlocked() # Open the HDF store and return the DataFrame diff --git a/policyengine_us_data/datasets/scf/scf.py b/policyengine_us_data/datasets/scf/scf.py index caac23ee..b3c97f1e 100644 --- a/policyengine_us_data/datasets/scf/scf.py +++ b/policyengine_us_data/datasets/scf/scf.py @@ -82,7 +82,7 @@ def _load_dataset_unlocked(self): """ # Check if file exists if not os.path.exists(self.file_path): - print(f"SCF dataset file not found. Generating it.") + print("SCF dataset file not found. Generating it.") self._generate_unlocked() # Open the HDF5 file and handle potential errors diff --git a/policyengine_us_data/db/validate_database.py b/policyengine_us_data/db/validate_database.py index 8f769d76..4e7ad5cc 100644 --- a/policyengine_us_data/db/validate_database.py +++ b/policyengine_us_data/db/validate_database.py @@ -15,11 +15,11 @@ targets_df = pd.read_sql("SELECT * FROM targets", conn) for var_name in set(targets_df["variable"]): - if not var_name in system.variables.keys(): + if var_name not in system.variables.keys(): raise ValueError(f"{var_name} not a policyengine-us variable") for var_name in set(stratum_constraints_df["constraint_variable"]): - if not var_name in system.variables.keys(): + if var_name not in system.variables.keys(): raise ValueError(f"{var_name} not a policyengine-us variable") TAX_EXPENDITURE_VARS = [ diff --git a/policyengine_us_data/db/validate_hierarchy.py b/policyengine_us_data/db/validate_hierarchy.py index 1c555703..5fd3bdab 100644 --- a/policyengine_us_data/db/validate_hierarchy.py +++ b/policyengine_us_data/db/validate_hierarchy.py @@ -153,7 +153,7 @@ def validate_geographic_hierarchy(session): if len(wyoming_cds) != 1: errors.append(f"ERROR: Wyoming should have 1 CD, found {len(wyoming_cds)}") else: - print(f"✓ Wyoming has correct number of CDs: 1") + print("✓ Wyoming has correct number of CDs: 1") # Verify no other state's CDs are incorrectly parented to Wyoming wrong_parent_cds = ( @@ -216,7 +216,7 @@ def validate_demographic_strata(session): domain_counts = {row[0]: row[1] for row in result} - print(f"\nDomain variables found via stratum_domain view:") + print("\nDomain variables found via stratum_domain view:") for domain, count in sorted(domain_counts.items()): print(f" {domain}: {count} strata") diff --git a/policyengine_us_data/pipeline_metadata.py b/policyengine_us_data/pipeline_metadata.py new file mode 100644 index 00000000..9e17d0b1 --- /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 00000000..fcb24cf6 --- /dev/null +++ b/policyengine_us_data/pipeline_schema.py @@ -0,0 +1,139 @@ +"""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 PipelineGroup: + """A visual wrapper around related nodes within a stage. + + Groups are used for orchestration functions that are intentionally + expanded into multiple documented substeps in the diagram. + + Args: + id: Unique identifier for the group. + label: Display name shown on the frame. + description: Optional explanation of what the wrapper does. + node_ids: IDs of stage nodes enclosed by the group. + """ + + id: str + label: str + description: str = "" + node_ids: list[str] = field(default_factory=list) + + +@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. + groups: Visual wrappers around related nodes. + """ + + 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) + groups: list[PipelineGroup] = field(default_factory=list) diff --git a/policyengine_us_data/storage/calibration_targets/audit_county_enum.py b/policyengine_us_data/storage/calibration_targets/audit_county_enum.py index fcaf443f..0c468a7c 100644 --- a/policyengine_us_data/storage/calibration_targets/audit_county_enum.py +++ b/policyengine_us_data/storage/calibration_targets/audit_county_enum.py @@ -83,7 +83,7 @@ def audit_county_enum(): else: invalid_entries["non_existent"].append((name, state)) - print(f"\nAudit Results:") + print("\nAudit Results:") print(f" Valid entries: {valid_count}") print( f" Wrong state: {len(invalid_entries['wrong_state'])} " diff --git a/policyengine_us_data/storage/calibration_targets/make_block_cd_distributions.py b/policyengine_us_data/storage/calibration_targets/make_block_cd_distributions.py index 6afaa2a6..cbeab9b8 100644 --- a/policyengine_us_data/storage/calibration_targets/make_block_cd_distributions.py +++ b/policyengine_us_data/storage/calibration_targets/make_block_cd_distributions.py @@ -115,7 +115,7 @@ def build_block_cd_distributions(): # Print some stats blocks_per_cd = output.groupby("cd_geoid").size() - print(f"\nBlocks per CD:") + print("\nBlocks per CD:") print(f" Min: {blocks_per_cd.min():,}") print(f" Median: {blocks_per_cd.median():,.0f}") print(f" Max: {blocks_per_cd.max():,}") diff --git a/policyengine_us_data/storage/calibration_targets/make_block_crosswalk.py b/policyengine_us_data/storage/calibration_targets/make_block_crosswalk.py index 975ba5e2..cde30484 100644 --- a/policyengine_us_data/storage/calibration_targets/make_block_crosswalk.py +++ b/policyengine_us_data/storage/calibration_targets/make_block_crosswalk.py @@ -214,7 +214,7 @@ def build_block_crosswalk(): print(f" File size: {output_path.stat().st_size / 1024 / 1024:.1f} MB") # Stats - print(f"\nCoverage:") + print("\nCoverage:") print(f" Blocks with SLDU: {combined['sldu'].notna().sum():,}") print(f" Blocks with SLDL: {combined['sldl'].notna().sum():,}") print(f" Blocks with Place: {combined['place_fips'].notna().sum():,}") diff --git a/policyengine_us_data/storage/fetch_cd_rents.py b/policyengine_us_data/storage/fetch_cd_rents.py index 719a32a6..9a797f60 100644 --- a/policyengine_us_data/storage/fetch_cd_rents.py +++ b/policyengine_us_data/storage/fetch_cd_rents.py @@ -42,7 +42,7 @@ "median_2br_rent": float(row[0]) if row[0] else None, } ) - except Exception as e: + except Exception: pass df = pd.DataFrame(all_rows) diff --git a/policyengine_us_data/utils/mortgage_interest.py b/policyengine_us_data/utils/mortgage_interest.py index af26974e..ba174a9e 100644 --- a/policyengine_us_data/utils/mortgage_interest.py +++ b/policyengine_us_data/utils/mortgage_interest.py @@ -5,6 +5,9 @@ import numpy as np import pandas as pd +from policyengine_us_data.pipeline_metadata import pipeline_node +from policyengine_us_data.pipeline_schema import PipelineNode + STRUCTURAL_MORTGAGE_VARIABLES = ( "first_home_mortgage_balance", @@ -33,6 +36,16 @@ ] +@pipeline_node( + PipelineNode( + id="mortgage_hints", + label="Mortgage Balance Hint Imputation", + node_type="process", + description="Impute tax-unit mortgage balance hints from SCF donor balances", + details="Fits a weighted QRF on SCF mortgage holders, predicts first-lien and secondary acquisition-debt balance hints, and enforces conservative nonnegative ordering", + source_file="policyengine_us_data/utils/mortgage_interest.py", + ) +) def impute_tax_unit_mortgage_balance_hints( data: Dict[str, Dict[int, np.ndarray]], time_period: int, @@ -99,6 +112,16 @@ def impute_tax_unit_mortgage_balance_hints( return data +@pipeline_node( + PipelineNode( + id="mortgage_convert", + label="Structural Mortgage Conversion", + node_type="process", + description="Convert deductible mortgage interest into structural mortgage balances, interest, and origination-year inputs", + details="Preserves current-law deductible mortgage and total interest deductions while deriving first-lien, secondary acquisition-debt, and non-mortgage residual interest inputs", + source_file="policyengine_us_data/utils/mortgage_interest.py", + ) +) def convert_mortgage_interest_to_structural_inputs( data: Dict[str, Dict[int, np.ndarray]], time_period: int, diff --git a/restage.py b/restage.py index 8333d3aa..d96b20b1 100644 --- a/restage.py +++ b/restage.py @@ -25,6 +25,6 @@ def restage(): print(f" Districts: {manifest['totals']['districts']}") print(f" Cities: {manifest['totals']['cities']}") - print(f"\nUploading to HF staging...") + print("\nUploading to HF staging...") result = upload_to_staging.remote(branch=branch, version=version, manifest=manifest) print(result) diff --git a/scripts/extract_pipeline.py b/scripts/extract_pipeline.py new file mode 100644 index 00000000..08a78283 --- /dev/null +++ b/scripts/extract_pipeline.py @@ -0,0 +1,298 @@ +"""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/app/pipeline.json +""" + +import ast +import json +import sys +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" / "app" / "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", +} + +# Decorated code nodes that are intentionally not rendered as graph nodes. +# The diagrams document these flows through finer-grained YAML nodes instead. +IGNORED_CODE_NODE_IDS = { + "create_stratified": "Stage 3b shows this wrapper as a visual group around AGI/top-1%/sample steps", + "run_calibration": "Stages 5-6 show this wrapper as visual groups around build/fit/output steps", +} + + +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 = [] + used_code_node_ids = set() + 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": [], + "groups": stage_def.get("groups", []), + } + + # 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) + existing_ids = {n["id"] for n in stage["nodes"]} + for edge in stage_def.get("edges", []): + for node_id in (edge["source"], edge["target"]): + if node_id not in all_code_nodes: + continue + used_code_node_ids.add(node_id) + if node_id not in existing_ids: + stage["nodes"].append(all_code_nodes[node_id]) + existing_ids.add(node_id) + + # 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 + unused_code_node_ids = set(all_code_nodes) - used_code_node_ids + ignored_unused = unused_code_node_ids & set(IGNORED_CODE_NODE_IDS) + unexpected_unused = unused_code_node_ids - set(IGNORED_CODE_NODE_IDS) + + for node_id in sorted(ignored_unused): + print( + f" INFO: Decorated node '{node_id}' is intentionally omitted: " + f"{IGNORED_CODE_NODE_IDS[node_id]}" + ) + + for node_id in sorted(unexpected_unused): + print( + f" ERROR: Decorated node '{node_id}' is not referenced by " + "pipeline_stages.yaml edges and is not in IGNORED_CODE_NODE_IDS", + file=sys.stderr, + ) + errors += 1 + + 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 + + for group in stage.get("groups", []): + missing_group_nodes = [ + node_id + for node_id in group.get("node_ids", []) + if node_id not in node_ids + ] + if missing_group_nodes: + print( + f" ERROR: Group '{group['id']}' references missing nodes " + f"in stage {stage['id']}: {', '.join(missing_group_nodes)}", + file=sys.stderr, + ) + errors += 1 + + if errors: + raise SystemExit(f"\n {errors} validation error(s) found") + + # Step 5: Build output + pipeline_json = { + "stages": stages_output, + "metadata": { + "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) + f.write("\n") + + 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)) diff --git a/tests/unit/calibration/create_test_fixture.py b/tests/unit/calibration/create_test_fixture.py index 2fadeeeb..85577fe1 100644 --- a/tests/unit/calibration/create_test_fixture.py +++ b/tests/unit/calibration/create_test_fixture.py @@ -172,7 +172,7 @@ def create_test_fixture(): # Verify with h5py.File(FIXTURE_PATH, "r") as f: - print(f"\nVerification:") + print("\nVerification:") print(f" Variables: {list(f.keys())}") print(f" household_id shape: {f['household_id'][TIME_PERIOD].shape}") print(f" person_id shape: {f['person_id'][TIME_PERIOD].shape}") diff --git a/tests/unit/test_extract_pipeline.py b/tests/unit/test_extract_pipeline.py new file mode 100644 index 00000000..78a35073 --- /dev/null +++ b/tests/unit/test_extract_pipeline.py @@ -0,0 +1,53 @@ +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest +from unittest.mock import patch + +from scripts import extract_pipeline + + +class ExtractPipelineValidationTest(unittest.TestCase): + def test_build_pipeline_json_rejects_missing_edge_target_without_groups(self): + stages_yaml = { + "stages": [ + { + "id": 0, + "label": "Stage 0", + "title": "Stage 0", + "description": "Validation test stage", + "extra_nodes": [ + { + "id": "existing", + "label": "Existing node", + "node_type": "input", + "description": "Present in the stage", + } + ], + "edges": [ + { + "source": "existing", + "target": "missing_target", + "edge_type": "data_flow", + } + ], + } + ] + } + + with TemporaryDirectory() as temp_dir: + with patch.object( + extract_pipeline, + "scan_source_files", + return_value={}, + ): + with patch.object( + extract_pipeline, + "load_stages_yaml", + return_value=stages_yaml, + ): + with self.assertRaises(SystemExit) as exc: + extract_pipeline.build_pipeline_json( + Path(temp_dir) / "pipeline.json" + ) + + self.assertEqual(str(exc.exception), "\n 1 validation error(s) found")