From d8484d5ef68a5a9f626582c1ab0c42df729ef825 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 28 Sep 2026 13:20:48 +0000 Subject: [PATCH 1/4] chore: dashboard health 2026-09-28 --- docs/health/STATUS.md | 33 ++++++++++++++++++++++++--------- docs/health/history.jsonl | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/docs/health/STATUS.md b/docs/health/STATUS.md index 347bea4..812710d 100644 --- a/docs/health/STATUS.md +++ b/docs/health/STATUS.md @@ -15,18 +15,33 @@ look wrong, there is a date to point at. ## development -**8 of 8 working** · 2026-09-14 21:44 UTC · `9ecbbe0` +**8 of 8 working** · 2026-09-28 13:12 UTC · `01a6899` | | Question | Time | Targets | Anchors | Reaches | Change since last run | | --- | --- | ---: | ---: | ---: | ---: | --- | -| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 22s | 29 | 11 | — | _first run_ | -| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 11s | 60 | 8 | — | _first run_ | -| ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 4s | 103 | 6 | — | _first run_ | -| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 14s | 123 | 882 | 15696 | _first run_ | -| ✅ | Maine Private Wells Near Landfills & DoD Sites | 19s | 751 | 38 | — | _first run_ | -| ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 14s | 2431 | 135 | — | _first run_ | -| ✅ | Maine Private Wells Near Airports & Air Transportation Sites (AFFF) | 14s | 4425 | 174 | — | _first run_ | -| ✅ | PFHpA Groundwater Samples Downstream from Facilities in Cumberland County | 159s | 49 | 268 | 362 | _first run_ | +| ✅ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 174s | 49 | 265 | 84 | _first run_ | +| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 26s | 60 | 8 | — | — | +| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 66s | 123 | 882 | 13988 | **reaches 15696 → 13988** | +| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 32s | 526 | 732 | — | _first run_ | +| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 5s | 29 | 11 | — | — | +| ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 3s | 103 | 6 | — | — | +| ✅ | Maine Private Wells Near Landfills & DoD Sites | 12s | 751 | 38 | — | — | +| ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 8s | 2431 | 135 | — | — | + +## main + +**8 of 8 working** · 2026-09-28 13:17 UTC · `ca2be08` + +| | Question | Time | Targets | Anchors | Reaches | Change since last run | +| --- | --- | ---: | ---: | ---: | ---: | --- | +| ✅ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 133s | 49 | 265 | 84 | _first run_ | +| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 6s | 60 | 8 | — | _first run_ | +| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 9s | 123 | 882 | 13988 | _first run_ | +| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 12s | 526 | 732 | — | _first run_ | +| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 3s | 29 | 11 | — | _first run_ | +| ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 2s | 103 | 6 | — | _first run_ | +| ✅ | Maine Private Wells Near Landfills & DoD Sites | 7s | 751 | 38 | — | _first run_ | +| ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 7s | 2431 | 135 | — | _first run_ | --- diff --git a/docs/health/history.jsonl b/docs/health/history.jsonl index f65349a..a5f5c67 100644 --- a/docs/health/history.jsonl +++ b/docs/health/history.jsonl @@ -6,3 +6,19 @@ {"runAt":"2026-09-14T21:44:17.124Z","ref":"development","commit":"9ecbbe0","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":14266,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} {"runAt":"2026-09-14T21:44:17.124Z","ref":"development","commit":"9ecbbe0","id":"wells-near-airports-maine","title":"Maine Private Wells Near Airports & Air Transportation Sites (AFFF)","attempts":1,"status":"success","ms":14222,"counts":{"FIND_TARGET_ENTITIES":4425,"GET_ANCHOR_DETAILS":174,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} {"runAt":"2026-09-14T21:44:17.124Z","ref":"development","commit":"9ecbbe0","id":"facilities-downstream-pfhpa-gw-cumberland","title":"PFHpA Groundwater Samples Downstream from Facilities in Cumberland County","attempts":1,"status":"success","ms":159024,"counts":{"FIND_TARGET_ENTITIES":49,"GET_ANCHOR_DETAILS":268,"GET_REGION_BOUNDARIES":16,"GET_FLOWLINE_GEOMETRIES":362},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"facilities-upstream-pfhpa-gw-cumberland","title":"Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County","attempts":1,"status":"success","ms":174079,"counts":{"FIND_TARGET_ENTITIES":49,"GET_ANCHOR_DETAILS":265,"GET_REGION_BOUNDARIES":16,"GET_FLOWLINE_GEOMETRIES":84},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"samples-near-landfill-dod-penobscot-knox","title":"Samples Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":26122,"counts":{"FIND_TARGET_ENTITIES":60,"GET_ANCHOR_DETAILS":8,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"samples-downstream-waste-indiana","title":"Samples Downstream of Waste Treatment Facilities in Indiana","attempts":1,"status":"success","ms":65580,"counts":{"FIND_TARGET_ENTITIES":123,"GET_ANCHOR_DETAILS":882,"GET_REGION_BOUNDARIES":92,"GET_FLOWLINE_GEOMETRIES":13988},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"facilities-upstream-streams-cook","title":"Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL","attempts":1,"status":"success","ms":31712,"counts":{"FIND_TARGET_ENTITIES":526,"GET_ANCHOR_DETAILS":732,"GET_REGION_BOUNDARIES":102},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"samples-near-agchem-maine","title":"Samples Near Agricultural Chemical Facilities in Maine","attempts":1,"status":"success","ms":4918,"counts":{"FIND_TARGET_ENTITIES":29,"GET_ANCHOR_DETAILS":11,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"waterbodies-near-landfill-dod-penobscot-knox","title":"Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":2508,"counts":{"FIND_TARGET_ENTITIES":103,"GET_ANCHOR_DETAILS":6,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"wells-near-landfill-dod-maine","title":"Maine Private Wells Near Landfills & DoD Sites","attempts":1,"status":"success","ms":12166,"counts":{"FIND_TARGET_ENTITIES":751,"GET_ANCHOR_DETAILS":38,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:12:21.508Z","ref":"development","commit":"01a6899","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":8184,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"facilities-upstream-pfhpa-gw-cumberland","title":"Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County","attempts":1,"status":"success","ms":133245,"counts":{"FIND_TARGET_ENTITIES":49,"GET_ANCHOR_DETAILS":265,"GET_REGION_BOUNDARIES":16,"GET_FLOWLINE_GEOMETRIES":84},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"samples-near-landfill-dod-penobscot-knox","title":"Samples Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":5761,"counts":{"FIND_TARGET_ENTITIES":60,"GET_ANCHOR_DETAILS":8,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"samples-downstream-waste-indiana","title":"Samples Downstream of Waste Treatment Facilities in Indiana","attempts":1,"status":"success","ms":9002,"counts":{"FIND_TARGET_ENTITIES":123,"GET_ANCHOR_DETAILS":882,"GET_REGION_BOUNDARIES":92,"GET_FLOWLINE_GEOMETRIES":13988},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"facilities-upstream-streams-cook","title":"Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL","attempts":1,"status":"success","ms":12162,"counts":{"FIND_TARGET_ENTITIES":526,"GET_ANCHOR_DETAILS":732,"GET_REGION_BOUNDARIES":102},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"samples-near-agchem-maine","title":"Samples Near Agricultural Chemical Facilities in Maine","attempts":1,"status":"success","ms":2552,"counts":{"FIND_TARGET_ENTITIES":29,"GET_ANCHOR_DETAILS":11,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"waterbodies-near-landfill-dod-penobscot-knox","title":"Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":2358,"counts":{"FIND_TARGET_ENTITIES":103,"GET_ANCHOR_DETAILS":6,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"wells-near-landfill-dod-maine","title":"Maine Private Wells Near Landfills & DoD Sites","attempts":1,"status":"success","ms":6635,"counts":{"FIND_TARGET_ENTITIES":751,"GET_ANCHOR_DETAILS":38,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":6766,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} From fec9f3eeaaaea7c2a311d7b0e302861cb24b00f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 Oct 2026 14:06:12 +0000 Subject: [PATCH 2/4] chore: dashboard health 2026-10-05 --- docs/health/STATUS.md | 34 +++++++++++++++++++--------------- docs/health/history.jsonl | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/docs/health/STATUS.md b/docs/health/STATUS.md index 812710d..a1c06da 100644 --- a/docs/health/STATUS.md +++ b/docs/health/STATUS.md @@ -15,33 +15,37 @@ look wrong, there is a date to point at. ## development -**8 of 8 working** · 2026-09-28 13:12 UTC · `01a6899` +**8 of 8 working** · 2026-10-05 13:54 UTC · `d8484d5` | | Question | Time | Targets | Anchors | Reaches | Change since last run | | --- | --- | ---: | ---: | ---: | ---: | --- | -| ✅ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 174s | 49 | 265 | 84 | _first run_ | -| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 26s | 60 | 8 | — | — | -| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 66s | 123 | 882 | 13988 | **reaches 15696 → 13988** | -| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 32s | 526 | 732 | — | _first run_ | -| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 5s | 29 | 11 | — | — | +| ⚠️ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 261s | 49 | 265 | 84 | **boundaries 16 → 0** | +| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 32s | 60 | 8 | — | — | +| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 44s | 123 | 882 | 13988 | — | +| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 57s | 526 | 732 | — | — | +| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 6s | 29 | 11 | — | — | | ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 3s | 103 | 6 | — | — | | ✅ | Maine Private Wells Near Landfills & DoD Sites | 12s | 751 | 38 | — | — | | ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 8s | 2431 | 135 | — | — | +**Incomplete answers:** + +- Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County — could not answer whole query + ## main -**8 of 8 working** · 2026-09-28 13:17 UTC · `ca2be08` +**8 of 8 working** · 2026-10-05 14:01 UTC · `ca2be08` | | Question | Time | Targets | Anchors | Reaches | Change since last run | | --- | --- | ---: | ---: | ---: | ---: | --- | -| ✅ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 133s | 49 | 265 | 84 | _first run_ | -| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 6s | 60 | 8 | — | _first run_ | -| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 9s | 123 | 882 | 13988 | _first run_ | -| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 12s | 526 | 732 | — | _first run_ | -| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 3s | 29 | 11 | — | _first run_ | -| ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 2s | 103 | 6 | — | _first run_ | -| ✅ | Maine Private Wells Near Landfills & DoD Sites | 7s | 751 | 38 | — | _first run_ | -| ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 7s | 2431 | 135 | — | _first run_ | +| ✅ | Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County | 162s | 49 | 265 | 84 | — | +| ✅ | Samples Near Landfills & DOD Sites in Penobscot and Knox County | 12s | 60 | 8 | — | — | +| ✅ | Samples Downstream of Waste Treatment Facilities in Indiana | 32s | 123 | 882 | 13988 | — | +| ✅ | Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL | 25s | 526 | 732 | — | — | +| ✅ | Samples Near Agricultural Chemical Facilities in Maine | 6s | 29 | 11 | — | — | +| ✅ | Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County | 2s | 103 | 6 | — | — | +| ✅ | Maine Private Wells Near Landfills & DoD Sites | 11s | 751 | 38 | — | — | +| ✅ | Maine Private Wells Near Wastewater Treatment Facilities | 8s | 2431 | 135 | — | — | --- diff --git a/docs/health/history.jsonl b/docs/health/history.jsonl index a5f5c67..cb4c3d1 100644 --- a/docs/health/history.jsonl +++ b/docs/health/history.jsonl @@ -22,3 +22,19 @@ {"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"waterbodies-near-landfill-dod-penobscot-knox","title":"Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":2358,"counts":{"FIND_TARGET_ENTITIES":103,"GET_ANCHOR_DETAILS":6,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} {"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"wells-near-landfill-dod-maine","title":"Maine Private Wells Near Landfills & DoD Sites","attempts":1,"status":"success","ms":6635,"counts":{"FIND_TARGET_ENTITIES":751,"GET_ANCHOR_DETAILS":38,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} {"runAt":"2026-09-28T13:17:49.579Z","ref":"main","commit":"ca2be08","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":6766,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"facilities-upstream-pfhpa-gw-cumberland","title":"Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County","attempts":1,"status":"success","ms":261413,"counts":{"FIND_TARGET_ENTITIES":49,"GET_ANCHOR_DETAILS":265,"GET_FLOWLINE_GEOMETRIES":84},"partialFailed":["whole query"],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"samples-near-landfill-dod-penobscot-knox","title":"Samples Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":32149,"counts":{"FIND_TARGET_ENTITIES":60,"GET_ANCHOR_DETAILS":8,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"samples-downstream-waste-indiana","title":"Samples Downstream of Waste Treatment Facilities in Indiana","attempts":1,"status":"success","ms":43874,"counts":{"FIND_TARGET_ENTITIES":123,"GET_ANCHOR_DETAILS":882,"GET_REGION_BOUNDARIES":92,"GET_FLOWLINE_GEOMETRIES":13988},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"facilities-upstream-streams-cook","title":"Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL","attempts":1,"status":"success","ms":57382,"counts":{"FIND_TARGET_ENTITIES":526,"GET_ANCHOR_DETAILS":732,"GET_REGION_BOUNDARIES":102},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"samples-near-agchem-maine","title":"Samples Near Agricultural Chemical Facilities in Maine","attempts":1,"status":"success","ms":6061,"counts":{"FIND_TARGET_ENTITIES":29,"GET_ANCHOR_DETAILS":11,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"waterbodies-near-landfill-dod-penobscot-knox","title":"Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":2691,"counts":{"FIND_TARGET_ENTITIES":103,"GET_ANCHOR_DETAILS":6,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"wells-near-landfill-dod-maine","title":"Maine Private Wells Near Landfills & DoD Sites","attempts":1,"status":"success","ms":12133,"counts":{"FIND_TARGET_ENTITIES":751,"GET_ANCHOR_DETAILS":38,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T13:54:47.766Z","ref":"development","commit":"d8484d5","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":8121,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"facilities-upstream-pfhpa-gw-cumberland","title":"Facilities Upstream from PFHpA Groundwater Contamination in Cumberland County","attempts":1,"status":"success","ms":161946,"counts":{"FIND_TARGET_ENTITIES":49,"GET_ANCHOR_DETAILS":265,"GET_REGION_BOUNDARIES":16,"GET_FLOWLINE_GEOMETRIES":84},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"samples-near-landfill-dod-penobscot-knox","title":"Samples Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":11785,"counts":{"FIND_TARGET_ENTITIES":60,"GET_ANCHOR_DETAILS":8,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"samples-downstream-waste-indiana","title":"Samples Downstream of Waste Treatment Facilities in Indiana","attempts":1,"status":"success","ms":31682,"counts":{"FIND_TARGET_ENTITIES":123,"GET_ANCHOR_DETAILS":882,"GET_REGION_BOUNDARIES":92,"GET_FLOWLINE_GEOMETRIES":13988},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"facilities-upstream-streams-cook","title":"Chemical & Plastics Manufacturers Upstream from Streams in Cook County, IL","attempts":1,"status":"success","ms":24644,"counts":{"FIND_TARGET_ENTITIES":526,"GET_ANCHOR_DETAILS":732,"GET_REGION_BOUNDARIES":102},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"samples-near-agchem-maine","title":"Samples Near Agricultural Chemical Facilities in Maine","attempts":1,"status":"success","ms":5756,"counts":{"FIND_TARGET_ENTITIES":29,"GET_ANCHOR_DETAILS":11,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"waterbodies-near-landfill-dod-penobscot-knox","title":"Surface Water Bodies Near Landfills & DOD Sites in Penobscot and Knox County","attempts":1,"status":"success","ms":2498,"counts":{"FIND_TARGET_ENTITIES":103,"GET_ANCHOR_DETAILS":6,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"wells-near-landfill-dod-maine","title":"Maine Private Wells Near Landfills & DoD Sites","attempts":1,"status":"success","ms":11054,"counts":{"FIND_TARGET_ENTITIES":751,"GET_ANCHOR_DETAILS":38,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} +{"runAt":"2026-10-05T14:01:55.352Z","ref":"main","commit":"ca2be08","id":"wells-near-wwtp-maine","title":"Maine Private Wells Near Wastewater Treatment Facilities","attempts":1,"status":"success","ms":8060,"counts":{"FIND_TARGET_ENTITIES":2431,"GET_ANCHOR_DETAILS":135,"GET_REGION_BOUNDARIES":16},"partialFailed":[],"partialSkipped":[],"error":null} From 92d639e90f61d5083a5f22885a4457cd4d23377e Mon Sep 17 00:00:00 2001 From: Prayas Lashkari Date: Fri, 9 Oct 2026 14:33:29 -0400 Subject: [PATCH 3/4] feat: show the full SPARQL behind prebuilt questions The executor now records every query it sends (step, endpoint, slice, error if it failed), and the cached result carries that list. Prebuilt questions get a "See Full SPARQL" chip at the bottom left of the map that opens each query in a copyable code block, on success and failure. WIRE_VERSION is bumped to w2 so older cached entries, which have no queries, are skipped. Re-warm the cache after deploying. Adds Vitest and npm test, with a round-trip test for the wire format. --- package-lock.json | 452 ++++++++++++++++++++++++- package.json | 6 +- src/App.css | 121 +++++++ src/components/Layout/MainContent.tsx | 2 + src/components/Pipeline/SparqlChip.tsx | 114 +++++++ src/engine/cacheKey.ts | 2 +- src/engine/executor.ts | 38 ++- src/engine/wire.test.ts | 20 ++ src/engine/wire.ts | 6 +- 9 files changed, 751 insertions(+), 10 deletions(-) create mode 100644 src/components/Pipeline/SparqlChip.tsx create mode 100644 src/engine/wire.test.ts diff --git a/package-lock.json b/package-lock.json index e4f6eb5..c67ca75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,8 @@ "tsx": "^4.23.13", "typescript": "~5.9.3", "typescript-eslint": "^8.48.0", - "vite": "^7.3.1" + "vite": "^7.3.1", + "vitest": "^3.2.7" } }, "node_modules/@babel/code-frame": { @@ -1608,6 +1609,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1976,6 +1995,121 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2040,6 +2174,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -2118,6 +2262,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2158,6 +2312,23 @@ "url": "https://www.paypal.me/kirilvatev" } }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2175,6 +2346,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2285,6 +2466,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2324,6 +2515,13 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", @@ -2563,6 +2761,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2573,6 +2781,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.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", @@ -3007,6 +3225,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3017,6 +3242,16 @@ "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/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -3205,6 +3440,23 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3532,6 +3784,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -3551,6 +3810,20 @@ "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3564,6 +3837,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -3595,6 +3888,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3612,6 +3919,36 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", + "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -4318,6 +4655,102 @@ } } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4334,6 +4767,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 69af60f..bc7c07c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "check-query-joins": "tsx scripts/check-query-joins.mts", "check-query-snapshots": "tsx scripts/check-query-snapshots.mts", "query-matrix": "tsx scripts/query-matrix.mts", - "health-check": "tsx scripts/health-check.mts" + "health-check": "tsx scripts/health-check.mts", + "test": "vitest run" }, "dependencies": { "@tanstack/react-query": "^5.90.21", @@ -48,6 +49,7 @@ "tsx": "^4.23.13", "typescript": "~5.9.3", "typescript-eslint": "^8.48.0", - "vite": "^7.3.1" + "vite": "^7.3.1", + "vitest": "^3.2.7" } } diff --git a/src/App.css b/src/App.css index 87d1768..f53ce9c 100644 --- a/src/App.css +++ b/src/App.css @@ -2556,3 +2556,124 @@ font-weight: 600; cursor: pointer; } + +/* Sits where Leaflet's attribution would on the left, and above the 500 + error overlay so the SPARQL of a failed run is still one click away. */ +.sparql-chip { + position: absolute; + bottom: 0; + left: 0; + z-index: 900; + padding: 0.125rem 0.5rem; + background: rgba(255, 255, 255, 0.8); + border: none; + border-top-right-radius: var(--radius-sm, 4px); + color: var(--color-primary-700); + font-size: var(--text-xs, 0.75rem); + font-weight: 600; + cursor: pointer; +} + +.sparql-chip:hover { + background: white; + text-decoration: underline; +} + +.sparql-modal { + display: flex; + flex-direction: column; + width: 90%; + max-width: 900px; + max-height: 85vh; + background: white; + border-radius: var(--radius-lg); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25); + font-size: var(--text-sm); +} + +.sparql-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-gray-200); +} + +.sparql-modal-header h2 { + margin: 0; + font-size: var(--text-base); +} + +.sparql-modal-close { + padding: 0 0.375rem; + background: none; + border: none; + color: var(--color-gray-600); + font-size: 1.25rem; + line-height: 1; + cursor: pointer; +} + +.sparql-modal-body { + overflow-y: auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.sparql-block-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.375rem; + color: var(--color-gray-700); +} + +.sparql-block-header > span:first-child { + flex: 1; + min-width: 0; +} + +.sparql-block-failed { + padding: 0.0625rem 0.375rem; + background: var(--color-red-100); + border-radius: var(--radius-sm, 4px); + color: var(--color-red-700); + font-size: var(--text-xs, 0.75rem); + font-weight: 600; +} + +.sparql-copy { + display: inline-flex; + align-items: center; + flex-shrink: 0; + padding: 0.25rem 0.5rem; + background: var(--color-primary-50); + border: 1px solid var(--color-primary-200); + border-radius: var(--radius-sm, 4px); + color: var(--color-primary-700); + font-size: var(--text-xs, 0.75rem); + font-weight: 600; + cursor: pointer; +} + +.sparql-block-error { + margin: 0 0 0.375rem; + color: var(--color-red-700); + font-size: var(--text-xs, 0.75rem); +} + +.sparql-block pre { + margin: 0; + max-height: 320px; + overflow: auto; + padding: 0.75rem; + background: var(--color-gray-50); + border: 1px solid var(--color-gray-200); + border-radius: var(--radius-md); + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: var(--text-xs, 0.75rem); + line-height: 1.5; + white-space: pre; +} diff --git a/src/components/Layout/MainContent.tsx b/src/components/Layout/MainContent.tsx index 604894e..4c04637 100644 --- a/src/components/Layout/MainContent.tsx +++ b/src/components/Layout/MainContent.tsx @@ -3,6 +3,7 @@ import { ResultsMap } from '../Map/ResultsMap'; import { PipelineProgressStrip } from '../Pipeline/PipelineProgressStrip'; import { PartialResultsNotice } from '../Pipeline/PartialResultsNotice'; import { CachedResultNotice } from '../Pipeline/CachedResultNotice'; +import { SparqlChip } from '../Pipeline/SparqlChip'; import { useQueryStore } from '../../store/queryStore'; import { useMapLayers } from '../../hooks/useMapLayers'; import { useQueryPipeline } from '../../hooks/useQueryPipeline'; @@ -29,6 +30,7 @@ export function MainContent() { + ); diff --git a/src/components/Pipeline/SparqlChip.tsx b/src/components/Pipeline/SparqlChip.tsx new file mode 100644 index 0000000..9d6f43b --- /dev/null +++ b/src/components/Pipeline/SparqlChip.tsx @@ -0,0 +1,114 @@ +import { useEffect, useState } from 'react'; +import { useQueryStore } from '../../store/queryStore'; +import { PREBUILT_QUERIES } from '../../constants/prebuiltQueries'; +import { canonicalQuestion } from '../../engine/cacheKey'; +import type { SentQuery } from '../../engine/executor'; + +// Prebuilt questions only, and only while unedited: an edited prebuilt is a +// different question, and its SPARQL is no longer the one we vouch for. +function useIsUneditedPrebuilt(): boolean { + const activeQueryId = useQueryStore((s) => s.activeQueryId); + const question = useQueryStore((s) => s.question); + const prebuilt = PREBUILT_QUERIES.find((q) => q.id === activeQueryId); + if (!prebuilt) return false; + return ( + JSON.stringify(canonicalQuestion(question)) === + JSON.stringify(canonicalQuestion(prebuilt.question)) + ); +} + +export function SparqlChip() { + const isPrebuilt = useIsUneditedPrebuilt(); + const isRunning = useQueryStore((s) => s.isRunning); + const queries = useQueryStore((s) => s.pipelineResult?.queries); + const [open, setOpen] = useState(false); + + if (!isPrebuilt || isRunning || !queries?.length) return null; + + return ( + <> + + {open && setOpen(false)} />} + + ); +} + +function SparqlModal({ queries, onClose }: { queries: SentQuery[]; onClose: () => void }) { + useEffect(() => { + const onKey = (e: KeyboardEvent) => e.key === 'Escape' && onClose(); + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [onClose]); + + return ( +
+
e.stopPropagation()} + role='dialog' + aria-modal='true' + aria-label='Full SPARQL' + > +
+

Full SPARQL

+ +
+
+ {queries.map((q, i) => ( + + ))} +
+
+
+ ); +} + +function SparqlBlock({ sent }: { sent: SentQuery }) { + const [copied, setCopied] = useState(false); + + const copy = async () => { + try { + await navigator.clipboard.writeText(sent.query.trim()); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { + /* clipboard unavailable */ + } + }; + + return ( +
+
+ + Step {sent.step + 1} · {sent.description} · {sent.endpoint} + {sent.scope && ` · ${sent.scope}`} + + {sent.error && Failed} + +
+ {sent.error &&

{sent.error}

} +
+        {sent.query.trim()}
+      
+
+ ); +} diff --git a/src/engine/cacheKey.ts b/src/engine/cacheKey.ts index 935b056..0df07b8 100644 --- a/src/engine/cacheKey.ts +++ b/src/engine/cacheKey.ts @@ -7,7 +7,7 @@ import type { AnalysisQuestion } from '../types/query'; // // Bump WIRE_VERSION when the set of result keys we store changes (see wire.ts) — // old entries would then be missing data the map expects. -const WIRE_VERSION = 'w1'; +const WIRE_VERSION = 'w2'; // w2: results carry the SPARQL that produced them // Bump DATA_VERSION after a knowledge-graph reload to make every existing key // unreachable in one move. diff --git a/src/engine/executor.ts b/src/engine/executor.ts index 38f34f0..ec271fa 100644 --- a/src/engine/executor.ts +++ b/src/engine/executor.ts @@ -1,5 +1,6 @@ import type { AnalysisQuestion } from '../types/query'; import type { SparqlRow } from '../types/sparql'; +import type { EndpointKey } from '../constants/endpoints'; import type { PipelineStep, PipelineContext } from './planner'; import type { Scope } from './scope'; import { executeSparql } from './sparqlClient'; @@ -15,17 +16,30 @@ export interface PartialFailure { skipped: string[]; } +// One request as it was actually sent, kept so the exact SPARQL behind a map +// can be shown and copied. A step that was split appears once per slice. +export interface SentQuery { + step: number; + description: string; + endpoint: EndpointKey; + scope?: string; + query: string; + error?: string; +} + export interface PipelineSuccess { status: 'success'; data: Record; // Set when some slices of the work failed but enough succeeded to show a map. partial?: PartialFailure[]; + queries?: SentQuery[]; } export interface PipelineEmpty { status: 'empty'; failedAtStep: number; message: string; + queries?: SentQuery[]; } export interface PipelineError { @@ -33,6 +47,7 @@ export interface PipelineError { failedAtStep: number; message: string; error: Error; + queries?: SentQuery[]; } export type PipelineResult = PipelineSuccess | PipelineEmpty | PipelineError; @@ -85,6 +100,7 @@ async function runStep( step: PipelineStep, context: PipelineContext, report: (chunksDone: number, chunksTotal: number) => void, + record: (sent: Omit) => void, ): Promise { const startedAt = Date.now(); const queue: (Scope | undefined)[] = step.initialScopes?.(context) ?? [undefined]; @@ -122,6 +138,7 @@ async function runStep( try { const result = await executeSparql(step.endpoint, query, { cache: step.cache }); + record({ scope: scope?.label, query }); // Slices can overlap — the same river reach is reached from several // anchors — so merge as a set. Whole-row identity is the right key: for // aggregate rows the GROUP BY key never spans slices (chunking always @@ -136,6 +153,7 @@ async function runStep( } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); lastError = error; + record({ scope: scope?.label, query, error: error.message }); const splittable = error instanceof SparqlError && isSplittable(error.kind); if (splittable && !scope) knownTooLarge.add(query); const smaller = splittable ? await step.divide?.(context, scope) : null; @@ -166,6 +184,7 @@ export async function executePipeline( results: {}, }; const partial: PartialFailure[] = []; + const queries: SentQuery[] = []; for (let i = 0; i < steps.length; i++) { const step = steps[i]; @@ -174,11 +193,16 @@ export async function executePipeline( let outcome: StepOutcome; try { - outcome = await runStep(step, context, (chunksDone, chunksTotal) => { - if (chunksTotal > 1) { - onProgress({ ...base, status: 'running', chunksDone, chunksTotal }); - } - }); + outcome = await runStep( + step, + context, + (chunksDone, chunksTotal) => { + if (chunksTotal > 1) { + onProgress({ ...base, status: 'running', chunksDone, chunksTotal }); + } + }, + (sent) => queries.push({ step: i, description: step.description, endpoint: step.endpoint, ...sent }), + ); } catch (err) { onProgress({ ...base, status: 'failed' }); return { @@ -186,6 +210,7 @@ export async function executePipeline( failedAtStep: i, message: `Error at step: ${step.description}`, error: err instanceof Error ? err : new Error(String(err)), + queries, }; } @@ -198,6 +223,7 @@ export async function executePipeline( failedAtStep: i, message: `Error at step: ${step.description}`, error: outcome.lastError ?? new Error('Query failed'), + queries, }; } if (missing.length > 0) { @@ -222,6 +248,7 @@ export async function executePipeline( status: 'empty', failedAtStep: i, message: `No results at step: ${step.description}`, + queries, }; } } @@ -248,5 +275,6 @@ export async function executePipeline( status: 'success', data: context.results, ...(partial.length > 0 ? { partial } : {}), + queries, }; } diff --git a/src/engine/wire.test.ts b/src/engine/wire.test.ts new file mode 100644 index 0000000..173eb61 --- /dev/null +++ b/src/engine/wire.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'vitest'; +import { fromWire, isWireResult, toWire } from './wire'; +import type { PipelineSuccess } from './executor'; + +describe('wire', () => { + it('keeps the SPARQL that produced a result through the cache round trip', () => { + const result: PipelineSuccess = { + status: 'success', + data: { FIND_TARGET_ENTITIES: [{ iri: 'http://example.org/a' }] }, + queries: [ + { step: 0, description: 'Finding samples', endpoint: 'federation', query: 'SELECT * {}' }, + { step: 3, description: 'Loading details', endpoint: 'federation', scope: 'batch 1', query: 'SELECT ?x {}', error: 'timeout' }, + ], + }; + const wire = JSON.parse(JSON.stringify(toWire(result))); + expect(isWireResult(wire)).toBe(true); + expect(fromWire(wire).queries).toEqual(result.queries); + }); + +}); diff --git a/src/engine/wire.ts b/src/engine/wire.ts index b221e29..ba618a8 100644 --- a/src/engine/wire.ts +++ b/src/engine/wire.ts @@ -1,5 +1,5 @@ import type { SparqlRow } from '../types/sparql'; -import type { PartialFailure, PipelineSuccess } from './executor'; +import type { PartialFailure, PipelineSuccess, SentQuery } from './executor'; // The shape a cached result takes over the wire and in Postgres. // @@ -10,6 +10,8 @@ export interface WireResult { status: 'success'; data: Record; partial?: PartialFailure[]; + // The SPARQL that produced this result, for the "See Full SPARQL" modal. + queries?: SentQuery[]; computedAt: string; } @@ -32,6 +34,7 @@ export function toWire(result: PipelineSuccess, computedAt = new Date().toISOStr status: 'success', data, ...(result.partial?.length ? { partial: result.partial } : {}), + ...(result.queries?.length ? { queries: result.queries } : {}), computedAt, }; } @@ -41,6 +44,7 @@ export function fromWire(wire: WireResult): PipelineSuccess { status: 'success', data: wire.data, ...(wire.partial?.length ? { partial: wire.partial } : {}), + ...(wire.queries?.length ? { queries: wire.queries } : {}), }; } From 7340a114ae7e8857d6fcee9f69bd906bb0d3871c Mon Sep 17 00:00:00 2001 From: Prayas Lashkari Date: Fri, 9 Oct 2026 15:22:43 -0400 Subject: [PATCH 4/4] style: make the See Full SPARQL chip a floating map control Lifted off the corner with a border, shadow and code icon, at the same text size as the saved-results notice, so it reads as a control rather than map credits. --- src/App.css | 26 +++++++++++++++----------- src/components/Pipeline/SparqlChip.tsx | 4 ++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/App.css b/src/App.css index f53ce9c..0e42462 100644 --- a/src/App.css +++ b/src/App.css @@ -2557,26 +2557,30 @@ cursor: pointer; } -/* Sits where Leaflet's attribution would on the left, and above the 500 - error overlay so the SPARQL of a failed run is still one click away. */ +/* Floats bottom-left like the map's own controls, and above the 500 error + overlay so the SPARQL of a failed run is still one click away. */ .sparql-chip { position: absolute; - bottom: 0; - left: 0; + bottom: 0.75rem; + left: 0.75rem; z-index: 900; - padding: 0.125rem 0.5rem; - background: rgba(255, 255, 255, 0.8); - border: none; - border-top-right-radius: var(--radius-sm, 4px); + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem 0.75rem; + background: white; + border: 1px solid var(--color-primary-200); + border-radius: var(--radius-md); + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12); color: var(--color-primary-700); - font-size: var(--text-xs, 0.75rem); + font-size: var(--text-sm); font-weight: 600; cursor: pointer; } .sparql-chip:hover { - background: white; - text-decoration: underline; + background: var(--color-primary-50); + border-color: var(--color-primary-700); } .sparql-modal { diff --git a/src/components/Pipeline/SparqlChip.tsx b/src/components/Pipeline/SparqlChip.tsx index 9d6f43b..ad982f4 100644 --- a/src/components/Pipeline/SparqlChip.tsx +++ b/src/components/Pipeline/SparqlChip.tsx @@ -28,6 +28,10 @@ export function SparqlChip() { return ( <> {open && setOpen(false)} />}