From 84cc2a523ff4592425829b48c581fdb5da731e96 Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Fri, 6 Jun 2025 14:06:56 +0100 Subject: [PATCH 1/8] feat(spectral): enforce presence of valid x-leading-capability in info block --- .../must/must-have-info-leading-capability.md | 333 ++++++++++++++++++ example/example.1.0.0.oas.yml | 1 + src/functions/has-valid-leading-capability.ts | 53 +++ src/functions/ukhsa-business-capabilities.ts | 311 ++++++++++++++++ ukhsa.oas.rules.yml | 14 + 5 files changed, 712 insertions(+) create mode 100644 docs/spectral-rules/must/must-have-info-leading-capability.md create mode 100644 src/functions/has-valid-leading-capability.ts create mode 100644 src/functions/ukhsa-business-capabilities.ts diff --git a/docs/spectral-rules/must/must-have-info-leading-capability.md b/docs/spectral-rules/must/must-have-info-leading-capability.md new file mode 100644 index 00000000..95888aa7 --- /dev/null +++ b/docs/spectral-rules/must/must-have-info-leading-capability.md @@ -0,0 +1,333 @@ +# **MUST** have info leading capability + +The `info` object **MUST** include an `x-leading-capability` field. + +The value MUST match one of the approved UKHSA Business Capabilities, which are defined in the controlled list. + +## Valid Capabilities + +Examples may include (but are not limited to): + + - Border Management + - Border Health Management + - Containment & quarantine management + - Immigration Screening + - Business Continuity Management + - Business Continuity Planning + - Recovery Management + - Change Management + - Benefits Management + - Change Governance + - Portfolio & Programme Management + - Chief Operating Officer + - Radiation, Chemical and Environmental Hazards + - Clinical and Science + - Behavioural Science + - Chemical Science + - Clinical Research + - Diagnostics + - Emerging Science Technology + - Environmental Science + - Epidemiological + - Immunology + - Microbiology Research + - Radiation Science + - Science Hub Management + - Serology + - Service Evaluation + - Whole genome sequencing + - Commercial + - Commercial Strategy + - Procurement & Contract Management + - Supplier Management + - Communication + - Corporate Affairs & Media Communications + - Emergency & External Communications + - Internal Communications + - Communications + - Internal & External Communications + - Contact Centre Data Management + - Contact Centre Management + - Contact Tracing + - Digital Engagement Management + - Operations Management + - Service Centre Management + - Corporate Affairs + - Corporate Functions + - Corporate and Governance + - Clinical Governance + - Corporate Governance + - Legal Advice + - Performance Management + - Quality Assurance + - Regulatory Compliance + - Research Governance + - Secretariat Management + - Customer & Channel Management + - Beneficiary Insight + - Channel Design + - Complaints & Queries + - Customer Information Management + - Customer Insight + - Customer Segmentation + - Data Analytics + - Bioinformatics & Genomics + - Data Engineering (inc. exploitation) + - Data Science + - Data Visualisation + - Official Statistics + - Predictive Analytics + - Data Operations + - Blood Safety, Hepatitis, Sexually Transmitted Infections Service (STIS) & HIV + - Healthcare Acquired Infections & Anti-Microbial Resistance + - Infections Research & Development + - Local Microbiology Laboratories & Infection Specialist Services + - National Immunisation + - National Specialist Surveillance & Reference Laboratories + - Rare Zoonotic Infections, Gastrointestinal Infections and Associated Areas + - Surveillance Systems & Bioinformatics Division + - Vaccines & Countermeasures + - Data Strategy + - Data Principles, Standards & Assurance + - Deliver Health Protection + - Chemical Health Protection Management + - Counter Measures Management + - Environmental Health Protection Management + - Global Operations + - Infectious Disease Health Protection Management + - National Operations + - Radiation HP Management + - Regional Service Delivery + - Tracing + - Emergency Resilience + - Emergency Response + - Emergency Response / Global Engagement + - Pandemic Management + - Readiness Management + - Resilience Response Readiness + - Surge Capacity Planning + - Engagement Management + - Global Engagement + - Local Authority Engagement Strategy + - Pan Regional Engagement Strategy + - Parliamentary Engagement + - Parliamentary Engagement Strategy + - Public Engagement + - Enterprise Management + - Asset Management + - Audit Management + - Change Management + - Commercial management + - Demand Management + - Enterprise Architecture Management + - Architectural Governance + - Data Governance + - Estates Management + - Facilities Management + - Financial Governance + - Financial management + - Environmental, Health & Infection Monitoring + - Emerging Infections Monitoring + - Environmental Monitoring + - Environmental, Health & Infection Monitoring / Local Authority Engagement Strategy + - Estate Management + - Health & Safety Management + - Property Management + - Finance & Commercial + - Business Development + - Digital & Information Communication Technology (ICT) + - Estates & Facilities + - Financial Management & Financial Strategy & Services + - Procurement + - Financial Management + - Financial Governance + - Financial Operations + - Financial Planning + - Financial Reporting + - Governance management + - Health Equity Assurance + - Equity Monitoring Management + - Equity Research Management + - Health Improvement + - Alcohol, Drugs, Tobacco and Inclusion health + - Dental Public Health + - Diet, Obesity & Physical Activity + - Health and Justice + - Health Improvement Priority Programmes + - Health Intelligence + - National Disease Registration Service  + - National Screening Programmes, UKNSC + - Research, Translation & Innovation (Health Improvement) + - Research, Translation & Innovation (Health Protection) + - Screening Quality Assurance + - Screening Quality Improvement + - Sexual Health and HIV Services + - Health Marketing + - Health Marketing & Behavioural Change + - Health Protection & Medical + - Emergency Preparedness & Response (EPR) + - Global Public Health (Health Improvement) + - Global Public Health (Health Protection) + - Medical and Public Health Professional Leadership and Practice + - National Healthcare Public Health + - National Poisons Information Service + - UK Teratology Information Service + - Horizon Scanning & Analysis + - Clinical Analysis + - Scientific Analysis + - Incident Response + - Case & Incident Response Management + - Situation Management + - Information Management + - Data Creation & Management + - Forms and Data Collection + - Data Governance + - Data Quality Assurance + - Data Sharing + - Master Data Management + - Research & Development Innovation + - Knowledge Management + - Content & Asset Management + - Knowledge Governance + - Knowledge integration + - Lab Network Management + - High Throughput Lab Management + - Other lab management + - MI & Report Management + - Nursing, Maternity & Early Years + - Maternity and Early Years + - Nursing, Midwifery, AHP and Emergency Services Public Health Leadership + - Quality, Clinical Governance and Safeguarding + - Pandemic Response Unit + - Covid Response + - Partnership Management + - Devolved Administration Partnership Management + - Devolved Administrations Partnership Mgt + - Global Partnership Management + - Local, Regional & National Partnership Management + - People + - People Services + - Public Health Workforce + - People Management + - Organisation, Culture & Leadership Development + - Pay, Reward & Performance Management + - People Operations + - Recruitment & Sourcing + - Workforce Development + - People Strategy Management Plus + - Places & Regions + - Regional & Sub Regional Health Care Public Health (HCPH) + - Regional and Local Health and Wellbeing Advice and Support (DHSC) + - Regional and Local Health and Wellbeing Advice and Support (UKHSA) + - Regional and Local Health Protection + - Regional and Local Screening and Immunisation Commissioning Support and Expert Advice (embedded in NHSE) + - Policy Development + - Policy Implementation + - Policy Definition (internal/ external policy) + - Policy Definition (internal/external policy) + - Policy Implementation + - Public Health Advise, Guidance & Training + - Advice Management + - Guidance Distribution + - Public Health Training + - Risk Communication + - Reference Data Management + - Research Management + - Evidence Review & Management + - Research Design + - Research Dissemination + - Research Partnerships + - Research Standards + - Risk Management + - Corporate Risk Management + - Scientific & Clinical Risk Management + - Science Hub + - Science Hub Programme + - Scientific Modelling + - Data Modelling & Analysis + - Security Management + - Cyber Security + - Information Security + - Physical Security Management + - Service Design & Management + - Customer Experience + - Product Management + - Service Design + - Technology Innovation + - Strategy + - Health Economics & Modelling + - Strategy + - Strategy Development + - Business Planning + - Strategy Management + - Supply Chain Management + - SCM - Delivery Management + - SCM - Demand Management + - Test Supply Chain Management + - Vaccine Supply Chain + - Surveillance Management + - Detect Emerging Infections + - Infectious Disease Surveillance + - Local, National & Global Health Surveillance + - RCE Surveillance + - Surveillance Design + - Technology Management + - API Ecosystem Management + - Application Porfolio Management + - Collaboration and Teamwork + - Identity and Access Management + - IT Architecture & IT Strategy + - IT Operations & Service Desk + - Network Management + - Platform, Infrastructure & Hosting Management + - Cloud Financial Management (FinOps) + - Container Platform Governance + - Virtual Desktop Infrastructure Management + - Voice and Telephony + - Software, Release & Development + - DevOps & Continuous Integration/Delivery + - Unified Device Management + - Test & Trace + - Analytics + - Cloud + - Communications + - Contain + - Data Transfer + - Genomics + - Operations + - Productivity + - Testing + - Tracing + - Utility + - Testing + - Diagnostic Testing + - Forensic Testing + - Mass Testing + - Point of Care Testing + - Rapid Testing + - Vaccine Testing + - Threat Management + - Global Threat Monitoring + - Hazard Assessment + - Quantify Threat + - Threat Assessment + - Vaccines Management + - Vaccine Development + - Vaccine Partnerships + - Vaccine Production + +## Valid Example + +```yaml +info: + title: Test Results API + x-leading-capability: Border Management +``` + +## Invalid Example + +```yaml +info: + title: Test Results API + x-leading-capability: Random Innovation +``` \ No newline at end of file diff --git a/example/example.1.0.0.oas.yml b/example/example.1.0.0.oas.yml index 5045b65d..a9f89ab6 100644 --- a/example/example.1.0.0.oas.yml +++ b/example/example.1.0.0.oas.yml @@ -4,6 +4,7 @@ info: version: '1.0.0' description: This API is for obtaining or submitting test results. x-audience: public-external + x-leading-capability: Border Management x-contains-sensitive-data: false x-value-chain: detect contact: diff --git a/src/functions/has-valid-leading-capability.ts b/src/functions/has-valid-leading-capability.ts new file mode 100644 index 00000000..463128ea --- /dev/null +++ b/src/functions/has-valid-leading-capability.ts @@ -0,0 +1,53 @@ +'use strict'; + +/* +Leading Capability Requirement (UKHSA Standard): + +info: + x-leading-capability: + type: string + description: MUST match one of the UKHSA Business Capabilities + +This Spectral custom function validates that `info.x-leading-capability` +exists and matches one of the predefined capabilities in the controlled list. +*/ + +import type { + IFunction, + IFunctionResult, + RulesetFunctionContext, +} from '@stoplight/spectral-core'; + +import CAPABILITIES from './ukhsa-business-capabilities'; + +/** + * Spectral custom function to validate `info.x-leading-capability` field. + */ +const validateLeadingCapability = function ( + targetVal: string | null | undefined, + opts: Record, + context: RulesetFunctionContext +): IFunctionResult[] { + if (typeof targetVal !== 'string') { + return [ + { + message: '`x-leading-capability` must be a string.', + path: context.path, + }, + ]; + } + + if (!CAPABILITIES.includes(targetVal)) { + return [ + { + message: + "Missing or wrong 'info.x-leading-capability', should be a valid UKHSA Business Capability.", + path: context.path, + }, + ]; + } + + return []; +}; + +export default validateLeadingCapability as IFunction; diff --git a/src/functions/ukhsa-business-capabilities.ts b/src/functions/ukhsa-business-capabilities.ts new file mode 100644 index 00000000..b654c870 --- /dev/null +++ b/src/functions/ukhsa-business-capabilities.ts @@ -0,0 +1,311 @@ +const CAPABILITIES = [ + "Border Management", + "Border Health Management", + "Containment & quarantine management", + "Immigration Screening", + "Business Continuity Management", + "Business Continuity Planning", + "Recovery Management", + "Change Management", + "Benefits Management", + "Change Governance", + "Portfolio & Programme Management", + "Chief Operating Officer", + "Radiation, Chemical and Environmental Hazards", + "Clinical and Science", + "Behavioural Science", + "Chemical Science", + "Clinical Research", + "Diagnostics", + "Emerging Science Technology", + "Environmental Science", + "Epidemiological", + "Immunology", + "Microbiology Research", + "Radiation Science", + "Science Hub Management", + "Serology", + "Service Evaluation", + "Whole genome sequencing", + "Commercial", + "Commercial Strategy", + "Procurement & Contract Management", + "Supplier Management", + "Communication", + "Corporate Affairs & Media Communications", + "Emergency & External Communications", + "Internal Communications", + "Communications", + "Internal & External Communications", + "Contact Centre Data Management", + "Contact Centre Management", + "Contact Tracing", + "Digital Engagement Management", + "Operations Management", + "Service Centre Management", + "Corporate Affairs", + "Corporate Functions", + "Corporate and Governance", + "Clinical Governance", + "Corporate Governance", + "Legal Advice", + "Performance Management", + "Quality Assurance", + "Regulatory Compliance", + "Research Governance", + "Secretariat Management", + "Customer & Channel Management", + "Beneficiary Insight", + "Channel Design", + "Complaints & Queries", + "Customer Information Management", + "Customer Insight", + "Customer Segmentation", + "Data Analytics", + "Bioinformatics & Genomics", + "Data Engineering (inc. exploitation)", + "Data Science", + "Data Visualisation", + "Official Statistics", + "Predictive Analytics", + "Data Operations", + "Blood Safety, Hepatitis, Sexually Transmitted Infections Service (STIS) & HIV", + "Healthcare Acquired Infections & Anti-Microbial Resistance", + "Infections Research & Development", + "Local Microbiology Laboratories & Infection Specialist Services", + "National Immunisation", + "National Specialist Surveillance & Reference Laboratories", + "Rare Zoonotic Infections, Gastrointestinal Infections and Associated Areas", + "Surveillance Systems & Bioinformatics Division", + "Vaccines & Countermeasures", + "Data Strategy", + "Data Principles, Standards & Assurance", + "Deliver Health Protection", + "Chemical Health Protection Management", + "Counter Measures Management", + "Environmental Health Protection Management", + "Global Operations", + "Infectious Disease Health Protection Management", + "National Operations", + "Radiation HP Management", + "Regional Service Delivery", + "Tracing", + "Emergency Resilience", + "Emergency Response", + "Emergency Response / Global Engagement", + "Pandemic Management", + "Readiness Management", + "Resilience Response Readiness", + "Surge Capacity Planning", + "Engagement Management", + "Global Engagement", + "Local Authority Engagement Strategy", + "Pan Regional Engagement Strategy", + "Parliamentary Engagement", + "Parliamentary Engagement Strategy", + "Public Engagement", + "Enterprise Management", + "Asset Management", + "Audit Management", + "Change Management", + "Commercial management", + "Demand Management", + "Enterprise Architecture Management", + "Architectural Governance", + "Data Governance", + "Estates Management", + "Facilities Management", + "Financial Governance", + "Financial management", + "Environmental, Health & Infection Monitoring", + "Emerging Infections Monitoring", + "Environmental Monitoring", + "Environmental, Health & Infection Monitoring / Local Authority Engagement Strategy", + "Estate Management", + "Health & Safety Management", + "Property Management", + "Finance & Commercial", + "Business Development", + "Digital & Information Communication Technology (ICT)", + "Estates & Facilities", + "Financial Management & Financial Strategy & Services", + "Procurement", + "Financial Management", + "Financial Governance", + "Financial Operations", + "Financial Planning", + "Financial Reporting", + "Governance management", + "Health Equity Assurance", + "Equity Monitoring Management", + "Equity Research Management", + "Health Improvement", + "Alcohol, Drugs, Tobacco and Inclusion health", + "Dental Public Health", + "Diet, Obesity & Physical Activity", + "Health and Justice", + "Health Improvement Priority Programmes", + "Health Intelligence", + "National Disease Registration Service ", + "National Screening Programmes, UKNSC", + "Research, Translation & Innovation (Health Improvement)", + "Research, Translation & Innovation (Health Protection)", + "Screening Quality Assurance", + "Screening Quality Improvement", + "Sexual Health and HIV Services", + "Health Marketing", + "Health Marketing & Behavioural Change", + "Health Protection & Medical", + "Emergency Preparedness & Response (EPR)", + "Global Public Health (Health Improvement)", + "Global Public Health (Health Protection)", + "Medical and Public Health Professional Leadership and Practice", + "National Healthcare Public Health", + "National Poisons Information Service", + "UK Teratology Information Service", + "Horizon Scanning & Analysis", + "Clinical Analysis", + "Scientific Analysis", + "Incident Response", + "Case & Incident Response Management", + "Situation Management", + "Information Management", + "Data Creation & Management", + "Forms and Data Collection", + "Data Governance", + "Data Quality Assurance", + "Data Sharing", + "Master Data Management", + "Research & Development Innovation", + "Knowledge Management", + "Content & Asset Management", + "Knowledge Governance", + "Knowledge integration", + "Lab Network Management", + "High Throughput Lab Management", + "Other lab management", + "MI & Report Management", + "Nursing, Maternity & Early Years", + "Maternity and Early Years", + "Nursing, Midwifery, AHP and Emergency Services Public Health Leadership", + "Quality, Clinical Governance and Safeguarding", + "Pandemic Response Unit", + "Covid Response", + "Partnership Management", + "Devolved Administration Partnership Management", + "Devolved Administrations Partnership Mgt", + "Global Partnership Management", + "Local, Regional & National Partnership Management", + "People", + "People Services", + "Public Health Workforce", + "People Management", + "Organisation, Culture & Leadership Development", + "Pay, Reward & Performance Management", + "People Operations", + "Recruitment & Sourcing", + "Workforce Development", + "People Strategy Management Plus", + "Places & Regions", + "Regional & Sub Regional Health Care Public Health (HCPH)", + "Regional and Local Health and Wellbeing Advice and Support (DHSC)", + "Regional and Local Health and Wellbeing Advice and Support (UKHSA)", + "Regional and Local Health Protection", + "Regional and Local Screening and Immunisation Commissioning Support and Expert Advice (embedded in NHSE)", + "Policy Development", + "Policy Implementation", + "Policy Definition (internal/ external policy)", + "Policy Definition (internal/external policy)", + "Policy Implementation", + "Public Health Advise, Guidance & Training", + "Advice Management", + "Guidance Distribution", + "Public Health Training", + "Risk Communication", + "Reference Data Management", + "Research Management", + "Evidence Review & Management", + "Research Design", + "Research Dissemination", + "Research Partnerships", + "Research Standards", + "Risk Management", + "Corporate Risk Management", + "Scientific & Clinical Risk Management", + "Science Hub", + "Science Hub Programme", + "Scientific Modelling", + "Data Modelling & Analysis", + "Security Management", + "Cyber Security", + "Information Security", + "Physical Security Management", + "Service Design & Management", + "Customer Experience", + "Product Management", + "Service Design", + "Technology Innovation", + "Strategy", + "Health Economics & Modelling", + "Strategy", + "Strategy Development", + "Business Planning", + "Strategy Management", + "Supply Chain Management", + "SCM - Delivery Management", + "SCM - Demand Management", + "Test Supply Chain Management", + "Vaccine Supply Chain", + "Surveillance Management", + "Detect Emerging Infections", + "Infectious Disease Surveillance", + "Local, National & Global Health Surveillance", + "RCE Surveillance", + "Surveillance Design", + "Technology Management", + "API Ecosystem Management", + "Application Porfolio Management", + "Collaboration and Teamwork", + "Identity and Access Management", + "IT Architecture & IT Strategy", + "IT Operations & Service Desk", + "Network Management", + "Platform, Infrastructure & Hosting Management", + "Cloud Financial Management (FinOps)", + "Container Platform Governance", + "Virtual Desktop Infrastructure Management", + "Voice and Telephony", + "Software, Release & Development", + "DevOps & Continuous Integration/Delivery", + "Unified Device Management", + "Test & Trace", + "Analytics", + "Cloud", + "Communications", + "Contain", + "Data Transfer", + "Genomics", + "Operations", + "Productivity", + "Testing", + "Tracing", + "Utility", + "Testing", + "Diagnostic Testing", + "Forensic Testing", + "Mass Testing", + "Point of Care Testing", + "Rapid Testing", + "Vaccine Testing", + "Threat Management", + "Global Threat Monitoring", + "Hazard Assessment", + "Quantify Threat", + "Threat Assessment", + "Vaccines Management", + "Vaccine Development", + "Vaccine Partnerships", + "Vaccine Production" +] + +export default CAPABILITIES; diff --git a/ukhsa.oas.rules.yml b/ukhsa.oas.rules.yml index b8257ecd..adc537d1 100644 --- a/ukhsa.oas.rules.yml +++ b/ukhsa.oas.rules.yml @@ -9,6 +9,7 @@ functions: - isApiInfoJsonSchema - hasRequiredProblemDetailsErrorResponses - overrideSeverity + - has-valid-leading-capability functionsDir: dist/functions @@ -357,3 +358,16 @@ rules: recommended: true description: 'APIs MAY include info.x-api-type to indicate API category. When present, it MUST be one of: standard, pygeoapi. When omitted, standard is assumed.' message: "Missing or wrong 'info.x-api-type'. Assuming 'standard'. Valid values: standard, pygeoapi." + + # MUST `info` object MUST include a valid `x-leading-capability` value from the controlled list of UKHSA Business Capabilities. + # => /docs/spectral-rules/must/must-have-info-leading-capability.md + + must-have-info-leading-capability: + description: 'The `info` object must have a `x-leading-capability` that matches a valid UKHSA Business Capability.' + message: "Missing or wrong `info.x-leading-capability`, should be a valid UKHSA Business Capability." + severity: error + given: $.info + then: + - function: has-valid-leading-capability + field: x-leading-capability + recommended: true From 022b7bb9a67bbe9394e9807e0f69df0d5fbb313a Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Mon, 7 Jul 2025 16:34:42 +0100 Subject: [PATCH 2/8] feat(spectral): switched rule to warn, cleaned-up of package.json, restored package-lock.json --- ukhsa.oas.rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ukhsa.oas.rules.yml b/ukhsa.oas.rules.yml index adc537d1..1dfc9d96 100644 --- a/ukhsa.oas.rules.yml +++ b/ukhsa.oas.rules.yml @@ -365,7 +365,7 @@ rules: must-have-info-leading-capability: description: 'The `info` object must have a `x-leading-capability` that matches a valid UKHSA Business Capability.' message: "Missing or wrong `info.x-leading-capability`, should be a valid UKHSA Business Capability." - severity: error + severity: warn given: $.info then: - function: has-valid-leading-capability From badf6260bf235b827005966dff4d8a97df6f7fbd Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Wed, 27 Aug 2025 14:39:09 +0100 Subject: [PATCH 3/8] fix: switched to correct business capabilities list, added script to generate it --- src/capabilities.py | 231 +++++++++++ src/functions/ukhsa-business-capabilities.ts | 388 ++++++------------- 2 files changed, 348 insertions(+), 271 deletions(-) create mode 100644 src/capabilities.py diff --git a/src/capabilities.py b/src/capabilities.py new file mode 100644 index 00000000..47ee3314 --- /dev/null +++ b/src/capabilities.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +""" +Extract lowest-level (leaf) Business Capabilities from a diagrams.net XML +file and optionally emit them into a TypeScript module. + +Example CLI usage: + python src/capabilities.py path/to/capabilities.xml \ + --ts-out src/functions/ukhsa-business-capabilities.ts +""" + +import argparse +import json +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + + +def extract_leaf_capabilities(xml_path: Path, *, sort: bool = True) -> list[str]: + """ + Parse a diagrams.net XML file and return the list of lowest-level + (leaf) Business Capabilities. + + Definition of a "leaf" capability: + • It is represented by an element with attributes + type="factSheet" and factSheetType="BusinessCapability". + • It is not the parent of any other BusinessCapability object (i.e., + no other BusinessCapability's references this object's id + via its "parent" attribute). + + Args: + xml_path: + Path to the XML file containing an root element. + sort: + If True (default), return labels sorted case-insensitively. + + Returns: + A list of unique capability labels (strings). Empty labels are ignored. + Duplicates (case-sensitive) are removed. + + Raises: + RuntimeError: + If the file cannot be read or the XML cannot be parsed. + + Notes: + This function is robust to typical diagrams.net exports where + Business Capabilities may be represented as "swimlanes" (containers) + and/or "labels" (children). Only BusinessCapability nodes + are considered. Parent/child linkage is inferred from the nested + attribute. + """ + try: + tree = ET.parse(xml_path) + root = tree.getroot() + except Exception as exc: + raise RuntimeError(f"Error reading/parsing XML: {exc}") from exc + + # Collect BusinessCapability objects and their parent references + bc_by_id: dict[str, dict[str, str | None]] = {} + parent_ids: set[str] = set() + + for obj in root.iter("object"): + if obj.get("type") != "factSheet": + continue + if obj.get("factSheetType") != "BusinessCapability": + continue + + obj_id = obj.get("id") + if not obj_id: + # Skip malformed objects without an id + continue + + label = (obj.get("label") or "").strip() + + parent: str | None = None + mxcell = obj.find("mxCell") + if mxcell is not None: + parent = mxcell.get("parent") + + bc_by_id[obj_id] = {"label": label, "parent": parent} + + # Identify which BCs act as parents of other BCs + for info in bc_by_id.values(): + parent = info["parent"] + if parent and parent in bc_by_id: + parent_ids.add(parent) + + # Leaf = BC that is not a parent of any other BC + leaf_ids: list[str] = [bid for bid in bc_by_id.keys() if bid not in parent_ids] + + # Build unique, non-empty labels + seen: set[str] = set() + labels: list[str] = [] + for bid in leaf_ids: + label = bc_by_id[bid]["label"] or "" + if label and label not in seen: + labels.append(label) + seen.add(label) + + if sort: + labels.sort(key=str.lower) + + return labels + + +def write_ts_file(capabilities: list[str], out_path: Path) -> None: + """ + Safely write a TypeScript file declaring and exporting a CAPABILITIES array. + + The emitted module has the following shape: + + const CAPABILITIES = [ + "Clinical Analysis", + "Scientific Analysis", + ... + ]; + + export default CAPABILITIES; + + Args: + capabilities: + The capability labels to emit. + out_path: + Destination file path for the TypeScript module. + + Behavior: + • Ensures the parent directory exists (if any). + • Writes to a temporary file first, then atomically replaces the target. + • Uses JSON string encoding for each element to safely handle quotes, + Unicode, and escape sequences. + + Raises: + OSError: + If the file cannot be written or replaced. + """ + if out_path.parent: + out_path.parent.mkdir(parents=True, exist_ok=True) + + # Serialize each capability as a JSON string literal to ensure valid TS. + lines = ",\n".join(f" {json.dumps(cap)}" for cap in capabilities) + ts_content = ( + "const CAPABILITIES = [\n" + f"{lines}\n" + "];\n\n" + "export default CAPABILITIES;\n" + ) + + tmp_path = out_path.with_suffix(out_path.suffix + ".tmp") + tmp_path.write_text(ts_content, encoding="utf-8") + + tmp_path.replace(out_path) + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + """ + Parse command-line arguments. + + Args: + argv: + Optional list of argument strings. If None, defaults to sys.argv[1:]. + + Returns: + argparse.Namespace with parsed arguments. + """ + parser = argparse.ArgumentParser( + description=( + "Extract lowest-level (leaf) Business Capabilities from an " + "diagrams.net XML file. Optionally emit a TypeScript module." + ) + ) + parser.add_argument( + "xml_path", + type=Path, + help="Path to the diagrams.net XML file (e.g., capabilities.xml).", + ) + parser.add_argument( + "--ts-out", + type=Path, + default=None, + help=( + "Optional path to write a TypeScript file, e.g., " + "src/functions/ukhsa-business-capabilities.ts" + ), + ) + parser.add_argument( + "--sort", + dest="sort", + action=argparse.BooleanOptionalAction, + default=True, + help="Sort capability labels alphabetically (default: enabled).", + ) + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> None: + """ + CLI entry point. + + Behavior: + • Extract leaf capabilities from the provided XML. + • Print them to stdout (one per line). + • If --ts-out is provided, also write the TypeScript module and print a + short confirmation to stderr. + + Exit codes: + 0 on success, non-zero on error. + """ + args = parse_args(argv) + + try: + caps = extract_leaf_capabilities(args.xml_path, sort=args.sort) + except Exception as exc: + print(str(exc), file=sys.stderr) + sys.exit(1) + + for cap in caps: + print(cap) + + if args.ts_out: + try: + write_ts_file(caps, args.ts_out) + print( + f"\n✅ Wrote {len(caps)} capabilities to {args.ts_out}", + file=sys.stderr, + ) + except OSError as exc: + print(f"Failed to write TypeScript file: {exc}", file=sys.stderr) + sys.exit(2) + + +if __name__ == "__main__": + main() diff --git a/src/functions/ukhsa-business-capabilities.ts b/src/functions/ukhsa-business-capabilities.ts index b654c870..60033f1f 100644 --- a/src/functions/ukhsa-business-capabilities.ts +++ b/src/functions/ukhsa-business-capabilities.ts @@ -1,311 +1,157 @@ const CAPABILITIES = [ - "Border Management", + "Advice Management", + "Behavioural Science", + "Beneficiary Insight", + "Benefits Management", + "Bioinformatics & Genomics", "Border Health Management", - "Containment & quarantine management", - "Immigration Screening", - "Business Continuity Management", "Business Continuity Planning", - "Recovery Management", - "Change Management", - "Benefits Management", + "Business Planning", + "Case & Incident Response Management", "Change Governance", - "Portfolio & Programme Management", - "Chief Operating Officer", - "Radiation, Chemical and Environmental Hazards", - "Clinical and Science", - "Behavioural Science", + "Channel Design", + "Chemical Health Protection Management", "Chemical Science", + "Clinical Analysis", + "Clinical Governance", "Clinical Research", - "Diagnostics", - "Emerging Science Technology", - "Environmental Science", - "Epidemiological", - "Immunology", - "Microbiology Research", - "Radiation Science", - "Science Hub Management", - "Serology", - "Service Evaluation", - "Whole genome sequencing", - "Commercial", "Commercial Strategy", - "Procurement & Contract Management", - "Supplier Management", - "Communication", - "Corporate Affairs & Media Communications", - "Emergency & External Communications", - "Internal Communications", - "Communications", - "Internal & External Communications", - "Contact Centre Data Management", - "Contact Centre Management", + "Complaints & Queries", "Contact Tracing", - "Digital Engagement Management", - "Operations Management", - "Service Centre Management", - "Corporate Affairs", - "Corporate Functions", - "Corporate and Governance", - "Clinical Governance", + "Containment & quarantine management", + "Content & Asset Management", + "Corporate Affairs & Media Communications", "Corporate Governance", - "Legal Advice", - "Performance Management", - "Quality Assurance", - "Regulatory Compliance", - "Research Governance", - "Secretariat Management", - "Customer & Channel Management", - "Beneficiary Insight", - "Channel Design", - "Complaints & Queries", + "Corporate Risk Management", + "Counter Measures Management", + "Customer Experience", "Customer Information Management", "Customer Insight", "Customer Segmentation", - "Data Analytics", - "Bioinformatics & Genomics", + "Cyber Security", + "Data Creation & Management", "Data Engineering (inc. exploitation)", + "Data Governance", + "Data Modelling & Analysis", + "Data Quality Assurance", "Data Science", + "Data Sharing", "Data Visualisation", - "Official Statistics", - "Predictive Analytics", - "Data Operations", - "Blood Safety, Hepatitis, Sexually Transmitted Infections Service (STIS) & HIV", - "Healthcare Acquired Infections & Anti-Microbial Resistance", - "Infections Research & Development", - "Local Microbiology Laboratories & Infection Specialist Services", - "National Immunisation", - "National Specialist Surveillance & Reference Laboratories", - "Rare Zoonotic Infections, Gastrointestinal Infections and Associated Areas", - "Surveillance Systems & Bioinformatics Division", - "Vaccines & Countermeasures", - "Data Strategy", - "Data Principles, Standards & Assurance", - "Deliver Health Protection", - "Chemical Health Protection Management", - "Counter Measures Management", - "Environmental Health Protection Management", - "Global Operations", - "Infectious Disease Health Protection Management", - "National Operations", - "Radiation HP Management", - "Regional Service Delivery", - "Tracing", - "Emergency Resilience", - "Emergency Response", - "Emergency Response / Global Engagement", - "Pandemic Management", - "Readiness Management", - "Resilience Response Readiness", - "Surge Capacity Planning", - "Engagement Management", - "Global Engagement", - "Local Authority Engagement Strategy", - "Pan Regional Engagement Strategy", - "Parliamentary Engagement", - "Parliamentary Engagement Strategy", - "Public Engagement", - "Enterprise Management", - "Asset Management", - "Audit Management", - "Change Management", - "Commercial management", - "Demand Management", - "Enterprise Architecture Management", - "Architectural Governance", - "Data Governance", - "Estates Management", - "Facilities Management", - "Financial Governance", - "Financial management", - "Environmental, Health & Infection Monitoring", + "Detect Emerging Infections", + "Devolved Administration Partnership Management", + "Devolved Administrations Partnership Mgt", + "Diagnostic Testing", + "Diagnostics", + "Digital Engagement Management", + "Emergency & External Communications", "Emerging Infections Monitoring", + "Emerging Science Technology", + "Environmental Health Protection Management", "Environmental Monitoring", - "Environmental, Health & Infection Monitoring / Local Authority Engagement Strategy", - "Estate Management", - "Health & Safety Management", - "Property Management", - "Finance & Commercial", - "Business Development", - "Digital & Information Communication Technology (ICT)", - "Estates & Facilities", - "Financial Management & Financial Strategy & Services", - "Procurement", - "Financial Management", + "Environmental Science", + "Epidemiological", + "Equity Monitoring Management", + "Equity Research Management", + "Evidence Review & Management", "Financial Governance", "Financial Operations", "Financial Planning", "Financial Reporting", - "Governance management", - "Health Equity Assurance", - "Equity Monitoring Management", - "Equity Research Management", - "Health Improvement", - "Alcohol, Drugs, Tobacco and Inclusion health", - "Dental Public Health", - "Diet, Obesity & Physical Activity", - "Health and Justice", - "Health Improvement Priority Programmes", - "Health Intelligence", - "National Disease Registration Service ", - "National Screening Programmes, UKNSC", - "Research, Translation & Innovation (Health Improvement)", - "Research, Translation & Innovation (Health Protection)", - "Screening Quality Assurance", - "Screening Quality Improvement", - "Sexual Health and HIV Services", - "Health Marketing", - "Health Marketing & Behavioural Change", - "Health Protection & Medical", - "Emergency Preparedness & Response (EPR)", - "Global Public Health (Health Improvement)", - "Global Public Health (Health Protection)", - "Medical and Public Health Professional Leadership and Practice", - "National Healthcare Public Health", - "National Poisons Information Service", - "UK Teratology Information Service", - "Horizon Scanning & Analysis", - "Clinical Analysis", - "Scientific Analysis", - "Incident Response", - "Case & Incident Response Management", - "Situation Management", - "Information Management", - "Data Creation & Management", - "Forms and Data Collection", - "Data Governance", - "Data Quality Assurance", - "Data Sharing", - "Master Data Management", - "Research & Development Innovation", - "Knowledge Management", - "Content & Asset Management", + "Forensic Testing", + "Global Engagement", + "Global Operations", + "Global Partnership Management", + "Global Threat Monitoring", + "Guidance Distribution", + "Hazard Assessment", + "Health & Safety Management", + "High Throughput Lab Management", + "Immigration Screening", + "Immunology", + "Infectious Disease Health Protection Management", + "Infectious Disease Surveillance", + "Information Security", + "Internal Communications", + "IT Architecture & IT Strategy", + "IT Operations & Service Desk", "Knowledge Governance", "Knowledge integration", - "Lab Network Management", - "High Throughput Lab Management", - "Other lab management", - "MI & Report Management", - "Nursing, Maternity & Early Years", - "Maternity and Early Years", - "Nursing, Midwifery, AHP and Emergency Services Public Health Leadership", - "Quality, Clinical Governance and Safeguarding", - "Pandemic Response Unit", - "Covid Response", - "Partnership Management", - "Devolved Administration Partnership Management", - "Devolved Administrations Partnership Mgt", - "Global Partnership Management", + "Legal Advice", + "Local Authority Engagement Strategy", + "Local, National & Global Health Surveillance", "Local, Regional & National Partnership Management", - "People", - "People Services", - "Public Health Workforce", - "People Management", - "Organisation, Culture & Leadership Development", - "Pay, Reward & Performance Management", + "Mass Testing", + "Microbiology Research", + "National Operations", + "Official Statistics", + "Operations Management", + "Organisation, Culture & Leadership Development", + "Other lab management", + "Pan Regional Engagement Strategy", + "Pandemic Management", + "Parliamentary Engagement", + "Parliamentary Engagement Strategy", + "Pay, Reward & Performance Management", "People Operations", - "Recruitment & Sourcing", - "Workforce Development", - "People Strategy Management Plus", - "Places & Regions", - "Regional & Sub Regional Health Care Public Health (HCPH)", - "Regional and Local Health and Wellbeing Advice and Support (DHSC)", - "Regional and Local Health and Wellbeing Advice and Support (UKHSA)", - "Regional and Local Health Protection", - "Regional and Local Screening and Immunisation Commissioning Support and Expert Advice (embedded in NHSE)", - "Policy Development", + "Performance Management", + "Physical Security Management", + "Platform, Infrastructure & Hosting Management", + "Point of Care Testing", "Policy Implementation", - "Policy Definition (internal/ external policy)", "Policy Definition (internal/external policy)", - "Policy Implementation", - "Public Health Advise, Guidance & Training", - "Advice Management", - "Guidance Distribution", + "Portfolio & Programme Management", + "Predictive Analytics", + "Procurement & Contract Management", + "Product Management", + "Property Management", + "Public Engagement", "Public Health Training", - "Risk Communication", - "Reference Data Management", - "Research Management", - "Evidence Review & Management", + "Quality Assurance", + "Quantify Threat", + "Radiation HP Management", + "Radiation Science", + "Rapid Testing", + "RCE Surveillance", + "Readiness Management", + "Recovery Management", + "Recruitment & Sourcing", + "Regional Service Delivery", + "Regulatory Compliance", + "Research & Development Innovation", "Research Design", "Research Dissemination", + "Research Governance", "Research Partnerships", "Research Standards", - "Risk Management", - "Corporate Risk Management", - "Scientific & Clinical Risk Management", - "Science Hub", - "Science Hub Programme", - "Scientific Modelling", - "Data Modelling & Analysis", - "Security Management", - "Cyber Security", - "Information Security", - "Physical Security Management", - "Service Design & Management", - "Customer Experience", - "Product Management", - "Service Design", - "Technology Innovation", - "Strategy", - "Health Economics & Modelling", - "Strategy", - "Strategy Development", - "Business Planning", - "Strategy Management", - "Supply Chain Management", + "Resilience Response Readiness", + "Risk Communication", + "Science Hub Management", + "Scientific & Clinical Risk Management", + "Scientific Analysis", "SCM - Delivery Management", "SCM - Demand Management", - "Test Supply Chain Management", - "Vaccine Supply Chain", - "Surveillance Management", - "Detect Emerging Infections", - "Infectious Disease Surveillance", - "Local, National & Global Health Surveillance", - "RCE Surveillance", + "Secretariat Management", + "Serology", + "Service Centre Management", + "Service Design", + "Service Evaluation", + "Situation Management", + "Software, Release & Development", + "Strategy Management", + "Supplier Management", + "Surge Capacity Planning", "Surveillance Design", - "Technology Management", - "API Ecosystem Management", - "Application Porfolio Management", - "Collaboration and Teamwork", - "Identity and Access Management", - "IT Architecture & IT Strategy", - "IT Operations & Service Desk", - "Network Management", - "Platform, Infrastructure & Hosting Management", - "Cloud Financial Management (FinOps)", - "Container Platform Governance", - "Virtual Desktop Infrastructure Management", - "Voice and Telephony", - "Software, Release & Development", - "DevOps & Continuous Integration/Delivery", - "Unified Device Management", - "Test & Trace", - "Analytics", - "Cloud", - "Communications", - "Contain", - "Data Transfer", - "Genomics", - "Operations", - "Productivity", - "Testing", - "Tracing", - "Utility", - "Testing", - "Diagnostic Testing", - "Forensic Testing", - "Mass Testing", - "Point of Care Testing", - "Rapid Testing", - "Vaccine Testing", - "Threat Management", - "Global Threat Monitoring", - "Hazard Assessment", - "Quantify Threat", + "Technology Innovation", + "Test Supply Chain Management", "Threat Assessment", - "Vaccines Management", + "Tracing", "Vaccine Development", "Vaccine Partnerships", - "Vaccine Production" -] + "Vaccine Production", + "Vaccine Supply Chain", + "Vaccine Testing", + "Whole genome sequencing", + "Workforce Development" +]; export default CAPABILITIES; From f61018dceb636e63fae0ec55bb5cd9b768d714ea Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Thu, 4 Sep 2025 16:35:33 +0100 Subject: [PATCH 4/8] refactor: converted the Business Capabilities parsing script from Python to TS --- .../must/must-have-info-leading-capability.md | 465 ++++++------------ package.json | 1 + src/capabilities.py | 231 --------- src/capabilities.ts | 394 +++++++++++++++ 4 files changed, 552 insertions(+), 539 deletions(-) delete mode 100644 src/capabilities.py create mode 100644 src/capabilities.ts diff --git a/docs/spectral-rules/must/must-have-info-leading-capability.md b/docs/spectral-rules/must/must-have-info-leading-capability.md index 95888aa7..68761352 100644 --- a/docs/spectral-rules/must/must-have-info-leading-capability.md +++ b/docs/spectral-rules/must/must-have-info-leading-capability.md @@ -8,313 +8,162 @@ The value MUST match one of the approved UKHSA Business Capabilities, which are Examples may include (but are not limited to): - - Border Management - - Border Health Management - - Containment & quarantine management - - Immigration Screening - - Business Continuity Management - - Business Continuity Planning - - Recovery Management - - Change Management - - Benefits Management - - Change Governance - - Portfolio & Programme Management - - Chief Operating Officer - - Radiation, Chemical and Environmental Hazards - - Clinical and Science - - Behavioural Science - - Chemical Science - - Clinical Research - - Diagnostics - - Emerging Science Technology - - Environmental Science - - Epidemiological - - Immunology - - Microbiology Research - - Radiation Science - - Science Hub Management - - Serology - - Service Evaluation - - Whole genome sequencing - - Commercial - - Commercial Strategy - - Procurement & Contract Management - - Supplier Management - - Communication - - Corporate Affairs & Media Communications - - Emergency & External Communications - - Internal Communications - - Communications - - Internal & External Communications - - Contact Centre Data Management - - Contact Centre Management - - Contact Tracing - - Digital Engagement Management - - Operations Management - - Service Centre Management - - Corporate Affairs - - Corporate Functions - - Corporate and Governance - - Clinical Governance - - Corporate Governance - - Legal Advice - - Performance Management - - Quality Assurance - - Regulatory Compliance - - Research Governance - - Secretariat Management - - Customer & Channel Management - - Beneficiary Insight - - Channel Design - - Complaints & Queries - - Customer Information Management - - Customer Insight - - Customer Segmentation - - Data Analytics - - Bioinformatics & Genomics - - Data Engineering (inc. exploitation) - - Data Science - - Data Visualisation - - Official Statistics - - Predictive Analytics - - Data Operations - - Blood Safety, Hepatitis, Sexually Transmitted Infections Service (STIS) & HIV - - Healthcare Acquired Infections & Anti-Microbial Resistance - - Infections Research & Development - - Local Microbiology Laboratories & Infection Specialist Services - - National Immunisation - - National Specialist Surveillance & Reference Laboratories - - Rare Zoonotic Infections, Gastrointestinal Infections and Associated Areas - - Surveillance Systems & Bioinformatics Division - - Vaccines & Countermeasures - - Data Strategy - - Data Principles, Standards & Assurance - - Deliver Health Protection - - Chemical Health Protection Management - - Counter Measures Management - - Environmental Health Protection Management - - Global Operations - - Infectious Disease Health Protection Management - - National Operations - - Radiation HP Management - - Regional Service Delivery - - Tracing - - Emergency Resilience - - Emergency Response - - Emergency Response / Global Engagement - - Pandemic Management - - Readiness Management - - Resilience Response Readiness - - Surge Capacity Planning - - Engagement Management - - Global Engagement - - Local Authority Engagement Strategy - - Pan Regional Engagement Strategy - - Parliamentary Engagement - - Parliamentary Engagement Strategy - - Public Engagement - - Enterprise Management - - Asset Management - - Audit Management - - Change Management - - Commercial management - - Demand Management - - Enterprise Architecture Management - - Architectural Governance - - Data Governance - - Estates Management - - Facilities Management - - Financial Governance - - Financial management - - Environmental, Health & Infection Monitoring - - Emerging Infections Monitoring - - Environmental Monitoring - - Environmental, Health & Infection Monitoring / Local Authority Engagement Strategy - - Estate Management - - Health & Safety Management - - Property Management - - Finance & Commercial - - Business Development - - Digital & Information Communication Technology (ICT) - - Estates & Facilities - - Financial Management & Financial Strategy & Services - - Procurement - - Financial Management - - Financial Governance - - Financial Operations - - Financial Planning - - Financial Reporting - - Governance management - - Health Equity Assurance - - Equity Monitoring Management - - Equity Research Management - - Health Improvement - - Alcohol, Drugs, Tobacco and Inclusion health - - Dental Public Health - - Diet, Obesity & Physical Activity - - Health and Justice - - Health Improvement Priority Programmes - - Health Intelligence - - National Disease Registration Service  - - National Screening Programmes, UKNSC - - Research, Translation & Innovation (Health Improvement) - - Research, Translation & Innovation (Health Protection) - - Screening Quality Assurance - - Screening Quality Improvement - - Sexual Health and HIV Services - - Health Marketing - - Health Marketing & Behavioural Change - - Health Protection & Medical - - Emergency Preparedness & Response (EPR) - - Global Public Health (Health Improvement) - - Global Public Health (Health Protection) - - Medical and Public Health Professional Leadership and Practice - - National Healthcare Public Health - - National Poisons Information Service - - UK Teratology Information Service - - Horizon Scanning & Analysis - - Clinical Analysis - - Scientific Analysis - - Incident Response - - Case & Incident Response Management - - Situation Management - - Information Management - - Data Creation & Management - - Forms and Data Collection - - Data Governance - - Data Quality Assurance - - Data Sharing - - Master Data Management - - Research & Development Innovation - - Knowledge Management - - Content & Asset Management - - Knowledge Governance - - Knowledge integration - - Lab Network Management - - High Throughput Lab Management - - Other lab management - - MI & Report Management - - Nursing, Maternity & Early Years - - Maternity and Early Years - - Nursing, Midwifery, AHP and Emergency Services Public Health Leadership - - Quality, Clinical Governance and Safeguarding - - Pandemic Response Unit - - Covid Response - - Partnership Management - - Devolved Administration Partnership Management - - Devolved Administrations Partnership Mgt - - Global Partnership Management - - Local, Regional & National Partnership Management - - People - - People Services - - Public Health Workforce - - People Management - - Organisation, Culture & Leadership Development - - Pay, Reward & Performance Management - - People Operations - - Recruitment & Sourcing - - Workforce Development - - People Strategy Management Plus - - Places & Regions - - Regional & Sub Regional Health Care Public Health (HCPH) - - Regional and Local Health and Wellbeing Advice and Support (DHSC) - - Regional and Local Health and Wellbeing Advice and Support (UKHSA) - - Regional and Local Health Protection - - Regional and Local Screening and Immunisation Commissioning Support and Expert Advice (embedded in NHSE) - - Policy Development - - Policy Implementation - - Policy Definition (internal/ external policy) - - Policy Definition (internal/external policy) - - Policy Implementation - - Public Health Advise, Guidance & Training - - Advice Management - - Guidance Distribution - - Public Health Training - - Risk Communication - - Reference Data Management - - Research Management - - Evidence Review & Management - - Research Design - - Research Dissemination - - Research Partnerships - - Research Standards - - Risk Management - - Corporate Risk Management - - Scientific & Clinical Risk Management - - Science Hub - - Science Hub Programme - - Scientific Modelling - - Data Modelling & Analysis - - Security Management - - Cyber Security - - Information Security - - Physical Security Management - - Service Design & Management - - Customer Experience - - Product Management - - Service Design - - Technology Innovation - - Strategy - - Health Economics & Modelling - - Strategy - - Strategy Development - - Business Planning - - Strategy Management - - Supply Chain Management - - SCM - Delivery Management - - SCM - Demand Management - - Test Supply Chain Management - - Vaccine Supply Chain - - Surveillance Management - - Detect Emerging Infections - - Infectious Disease Surveillance - - Local, National & Global Health Surveillance - - RCE Surveillance - - Surveillance Design - - Technology Management - - API Ecosystem Management - - Application Porfolio Management - - Collaboration and Teamwork - - Identity and Access Management - - IT Architecture & IT Strategy - - IT Operations & Service Desk - - Network Management - - Platform, Infrastructure & Hosting Management - - Cloud Financial Management (FinOps) - - Container Platform Governance - - Virtual Desktop Infrastructure Management - - Voice and Telephony - - Software, Release & Development - - DevOps & Continuous Integration/Delivery - - Unified Device Management - - Test & Trace - - Analytics - - Cloud - - Communications - - Contain - - Data Transfer - - Genomics - - Operations - - Productivity - - Testing - - Tracing - - Utility - - Testing - - Diagnostic Testing - - Forensic Testing - - Mass Testing - - Point of Care Testing - - Rapid Testing - - Vaccine Testing - - Threat Management - - Global Threat Monitoring - - Hazard Assessment - - Quantify Threat - - Threat Assessment - - Vaccines Management - - Vaccine Development - - Vaccine Partnerships - - Vaccine Production + + +- Advice Management +- Behavioural Science +- Beneficiary Insight +- Benefits Management +- Bioinformatics & Genomics +- Border Health Management +- Business Continuity Planning +- Business Planning +- Case & Incident Response Management +- Change Governance +- Channel Design +- Chemical Health Protection Management +- Chemical Science +- Clinical Analysis +- Clinical Governance +- Clinical Research +- Commercial Strategy +- Complaints & Queries +- Contact Tracing +- Containment & quarantine management +- Content & Asset Management +- Corporate Affairs & Media Communications +- Corporate Governance +- Corporate Risk Management +- Counter Measures Management +- Customer Experience +- Customer Information Management +- Customer Insight +- Customer Segmentation +- Cyber Security +- Data Creation & Management +- Data Engineering (inc. exploitation) +- Data Governance +- Data Modelling & Analysis +- Data Quality Assurance +- Data Science +- Data Sharing +- Data Visualisation +- Detect Emerging Infections +- Devolved Administration Partnership Management +- Devolved Administrations Partnership Mgt +- Diagnostic Testing +- Diagnostics +- Digital Engagement Management +- Emergency & External Communications +- Emerging Infections Monitoring +- Emerging Science Technology +- Environmental Health Protection Management +- Environmental Monitoring +- Environmental Science +- Epidemiological +- Equity Monitoring Management +- Equity Research Management +- Evidence Review & Management +- Financial Governance +- Financial Operations +- Financial Planning +- Financial Reporting +- Forensic Testing +- Global Engagement +- Global Operations +- Global Partnership Management +- Global Threat Monitoring +- Guidance Distribution +- Hazard Assessment +- Health & Safety Management +- High Throughput Lab Management +- Immigration Screening +- Immunology +- Infectious Disease Health Protection Management +- Infectious Disease Surveillance +- Information Security +- Internal Communications +- IT Architecture & IT Strategy +- IT Operations & Service Desk +- Knowledge Governance +- Knowledge integration +- Legal Advice +- Local Authority Engagement Strategy +- Local, National & Global Health Surveillance +- Local, Regional & National Partnership Management +- Mass Testing +- Microbiology Research +- National Operations +- Official Statistics +- Operations Management +- Organisation, Culture & Leadership Development +- Other lab management +- Pan Regional Engagement Strategy +- Pandemic Management +- Parliamentary Engagement +- Parliamentary Engagement Strategy +- Pay, Reward & Performance Management +- People Operations +- Performance Management +- Physical Security Management +- Platform, Infrastructure & Hosting Management +- Point of Care Testing +- Policy Implementation +- Policy Definition (internal/external policy) +- Portfolio & Programme Management +- Predictive Analytics +- Procurement & Contract Management +- Product Management +- Property Management +- Public Engagement +- Public Health Training +- Quality Assurance +- Quantify Threat +- Radiation HP Management +- Radiation Science +- Rapid Testing +- RCE Surveillance +- Readiness Management +- Recovery Management +- Recruitment & Sourcing +- Regional Service Delivery +- Regulatory Compliance +- Research & Development Innovation +- Research Design +- Research Dissemination +- Research Governance +- Research Partnerships +- Research Standards +- Resilience Response Readiness +- Risk Communication +- Science Hub Management +- Scientific & Clinical Risk Management +- Scientific Analysis +- SCM - Delivery Management +- SCM - Demand Management +- Secretariat Management +- Serology +- Service Centre Management +- Service Design +- Service Evaluation +- Situation Management +- Software, Release & Development +- Strategy Management +- Supplier Management +- Surge Capacity Planning +- Surveillance Design +- Technology Innovation +- Test Supply Chain Management +- Threat Assessment +- Tracing +- Vaccine Development +- Vaccine Partnerships +- Vaccine Production +- Vaccine Supply Chain +- Vaccine Testing +- Whole genome sequencing +- Workforce Development + ## Valid Example @@ -330,4 +179,4 @@ info: info: title: Test Results API x-leading-capability: Random Innovation -``` \ No newline at end of file +``` diff --git a/package.json b/package.json index 250d151d..be2aee0b 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@swc/jest": "^0.2.39", "@types/jest": "^30.0.0", "cpx2": "^8.0.0", + "fast-xml-parser": "^5.2.5", "husky": "^9.1.7", "jest": "^30.2.0", "jest-junit": "^16.0.0", diff --git a/src/capabilities.py b/src/capabilities.py deleted file mode 100644 index 47ee3314..00000000 --- a/src/capabilities.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python3 -""" -Extract lowest-level (leaf) Business Capabilities from a diagrams.net XML -file and optionally emit them into a TypeScript module. - -Example CLI usage: - python src/capabilities.py path/to/capabilities.xml \ - --ts-out src/functions/ukhsa-business-capabilities.ts -""" - -import argparse -import json -import sys -import xml.etree.ElementTree as ET -from pathlib import Path - - -def extract_leaf_capabilities(xml_path: Path, *, sort: bool = True) -> list[str]: - """ - Parse a diagrams.net XML file and return the list of lowest-level - (leaf) Business Capabilities. - - Definition of a "leaf" capability: - • It is represented by an element with attributes - type="factSheet" and factSheetType="BusinessCapability". - • It is not the parent of any other BusinessCapability object (i.e., - no other BusinessCapability's references this object's id - via its "parent" attribute). - - Args: - xml_path: - Path to the XML file containing an root element. - sort: - If True (default), return labels sorted case-insensitively. - - Returns: - A list of unique capability labels (strings). Empty labels are ignored. - Duplicates (case-sensitive) are removed. - - Raises: - RuntimeError: - If the file cannot be read or the XML cannot be parsed. - - Notes: - This function is robust to typical diagrams.net exports where - Business Capabilities may be represented as "swimlanes" (containers) - and/or "labels" (children). Only BusinessCapability nodes - are considered. Parent/child linkage is inferred from the nested - attribute. - """ - try: - tree = ET.parse(xml_path) - root = tree.getroot() - except Exception as exc: - raise RuntimeError(f"Error reading/parsing XML: {exc}") from exc - - # Collect BusinessCapability objects and their parent references - bc_by_id: dict[str, dict[str, str | None]] = {} - parent_ids: set[str] = set() - - for obj in root.iter("object"): - if obj.get("type") != "factSheet": - continue - if obj.get("factSheetType") != "BusinessCapability": - continue - - obj_id = obj.get("id") - if not obj_id: - # Skip malformed objects without an id - continue - - label = (obj.get("label") or "").strip() - - parent: str | None = None - mxcell = obj.find("mxCell") - if mxcell is not None: - parent = mxcell.get("parent") - - bc_by_id[obj_id] = {"label": label, "parent": parent} - - # Identify which BCs act as parents of other BCs - for info in bc_by_id.values(): - parent = info["parent"] - if parent and parent in bc_by_id: - parent_ids.add(parent) - - # Leaf = BC that is not a parent of any other BC - leaf_ids: list[str] = [bid for bid in bc_by_id.keys() if bid not in parent_ids] - - # Build unique, non-empty labels - seen: set[str] = set() - labels: list[str] = [] - for bid in leaf_ids: - label = bc_by_id[bid]["label"] or "" - if label and label not in seen: - labels.append(label) - seen.add(label) - - if sort: - labels.sort(key=str.lower) - - return labels - - -def write_ts_file(capabilities: list[str], out_path: Path) -> None: - """ - Safely write a TypeScript file declaring and exporting a CAPABILITIES array. - - The emitted module has the following shape: - - const CAPABILITIES = [ - "Clinical Analysis", - "Scientific Analysis", - ... - ]; - - export default CAPABILITIES; - - Args: - capabilities: - The capability labels to emit. - out_path: - Destination file path for the TypeScript module. - - Behavior: - • Ensures the parent directory exists (if any). - • Writes to a temporary file first, then atomically replaces the target. - • Uses JSON string encoding for each element to safely handle quotes, - Unicode, and escape sequences. - - Raises: - OSError: - If the file cannot be written or replaced. - """ - if out_path.parent: - out_path.parent.mkdir(parents=True, exist_ok=True) - - # Serialize each capability as a JSON string literal to ensure valid TS. - lines = ",\n".join(f" {json.dumps(cap)}" for cap in capabilities) - ts_content = ( - "const CAPABILITIES = [\n" - f"{lines}\n" - "];\n\n" - "export default CAPABILITIES;\n" - ) - - tmp_path = out_path.with_suffix(out_path.suffix + ".tmp") - tmp_path.write_text(ts_content, encoding="utf-8") - - tmp_path.replace(out_path) - - -def parse_args(argv: list[str] | None = None) -> argparse.Namespace: - """ - Parse command-line arguments. - - Args: - argv: - Optional list of argument strings. If None, defaults to sys.argv[1:]. - - Returns: - argparse.Namespace with parsed arguments. - """ - parser = argparse.ArgumentParser( - description=( - "Extract lowest-level (leaf) Business Capabilities from an " - "diagrams.net XML file. Optionally emit a TypeScript module." - ) - ) - parser.add_argument( - "xml_path", - type=Path, - help="Path to the diagrams.net XML file (e.g., capabilities.xml).", - ) - parser.add_argument( - "--ts-out", - type=Path, - default=None, - help=( - "Optional path to write a TypeScript file, e.g., " - "src/functions/ukhsa-business-capabilities.ts" - ), - ) - parser.add_argument( - "--sort", - dest="sort", - action=argparse.BooleanOptionalAction, - default=True, - help="Sort capability labels alphabetically (default: enabled).", - ) - return parser.parse_args(argv) - - -def main(argv: list[str] | None = None) -> None: - """ - CLI entry point. - - Behavior: - • Extract leaf capabilities from the provided XML. - • Print them to stdout (one per line). - • If --ts-out is provided, also write the TypeScript module and print a - short confirmation to stderr. - - Exit codes: - 0 on success, non-zero on error. - """ - args = parse_args(argv) - - try: - caps = extract_leaf_capabilities(args.xml_path, sort=args.sort) - except Exception as exc: - print(str(exc), file=sys.stderr) - sys.exit(1) - - for cap in caps: - print(cap) - - if args.ts_out: - try: - write_ts_file(caps, args.ts_out) - print( - f"\n✅ Wrote {len(caps)} capabilities to {args.ts_out}", - file=sys.stderr, - ) - except OSError as exc: - print(f"Failed to write TypeScript file: {exc}", file=sys.stderr) - sys.exit(2) - - -if __name__ == "__main__": - main() diff --git a/src/capabilities.ts b/src/capabilities.ts new file mode 100644 index 00000000..5c058159 --- /dev/null +++ b/src/capabilities.ts @@ -0,0 +1,394 @@ +#!/usr/bin/env node +/** + * Extract lowest-level (leaf) Business Capabilities from a diagrams.net XML + * file, optionally emit them into a TypeScript module, and/or update a + * Markdown file's "Valid Capabilities" section. + * + * Example CLI usage: + * npx ts-node src/capabilities.ts path/to/capabilities.xml \ + * --ts-out src/functions/ukhsa-business-capabilities.ts \ + * --md docs/must-have-info-leading-capability.md + */ + +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { XMLParser } from "fast-xml-parser"; + +type StrMap = Record; + +type CapabilityInfo = { + label: string; + parent: string | null; +}; + +type ExtractOptions = { + /** Whether to sort the labels alphabetically (default: true). */ + sort?: boolean; +}; + +/** + * Parse a diagrams.net XML file and return the list of lowest-level + * (leaf) Business Capabilities. + * + * **Definition of a "leaf" capability:** + * - Represented by an `` element with attributes + * `type="factSheet"` and `factSheetType="BusinessCapability"`. + * - Not the parent of any other BusinessCapability object (i.e., + * no other BusinessCapability’s `` references this object's id + * via its `"parent"` attribute). + * + * @param xmlPath Absolute or relative path to the diagrams.net XML file. + * @param opts Options controlling extraction behavior. + * @returns Array of unique capability labels. Empty labels are ignored. + * Duplicates (case-sensitive) are removed. + * @throws {Error} If the file cannot be read or the XML cannot be parsed. + * + * @remarks + * Robust to typical diagrams.net exports where Business Capabilities may be + * represented as containers and/or labels. Parent/child linkage is inferred + * from the nested `` attribute. + */ +export function extractLeafCapabilities( + xmlPath: string, + opts: ExtractOptions = { sort: true } +): string[] { + let xml: string; + try { + xml = fs.readFileSync(xmlPath, "utf8"); + } catch (e) { + throw new Error(`Error reading XML file: ${String(e)}`); + } + + const parser = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "@_", + allowBooleanAttributes: true, + isArray: (name) => name === "object", // force into array form + }); + + let root: StrMap; + try { + root = parser.parse(xml) as StrMap; + } catch (e) { + throw new Error(`Error parsing XML: ${String(e)}`); + } + + const objects: StrMap[] = []; + const stack: unknown[] = [root]; + while (stack.length) { + const node = stack.pop(); + if (!node || typeof node !== "object") continue; + const obj = node as StrMap; + + if (Array.isArray((obj as any).object)) { + objects.push(...((obj as any).object as StrMap[])); + } + + for (const v of Object.values(obj)) { + if (v && typeof v === "object") stack.push(v); + } + } + + const bcById = new Map(); + const parentIds = new Set(); + + for (const obj of objects) { + const type = (obj as any)["@_type"]; + const factSheetType = (obj as any)["@_factSheetType"]; + if (type !== "factSheet" || factSheetType !== "BusinessCapability") continue; + + const id = (obj as any)["@_id"] as string | undefined; + if (!id) continue; + + const rawLabel = ((obj as any)["@_label"] as string | undefined) ?? ""; + const label = rawLabel.trim(); + + const mxCell = (obj as any).mxCell as StrMap | undefined; + const parent = mxCell && typeof mxCell === "object" + ? ((mxCell as any)["@_parent"] as string | undefined) + : undefined; + + bcById.set(id, { label, parent: parent ?? null }); + } + + for (const info of bcById.values()) { + if (info.parent && bcById.has(info.parent)) { + parentIds.add(info.parent); + } + } + + const leafIds = [...bcById.keys()].filter((bid) => !parentIds.has(bid)); + + const seen = new Set(); + const labels: string[] = []; + for (const bid of leafIds) { + const info = bcById.get(bid)!; + const label = info.label ?? ""; + if (label && !seen.has(label)) { + labels.push(label); + seen.add(label); + } + } + + if (opts.sort !== false) { + labels.sort((a, b) => + a.localeCompare(b, undefined, { + sensitivity: "accent", + caseFirst: "lower", + }) + ); + } + + return labels; +} + +/** + * Write a TypeScript file declaring and exporting a `CAPABILITIES` array. + * + * Emits: + * ```ts + * const CAPABILITIES = [ + * "Clinical Analysis", + * "Scientific Analysis", + * // ... + * ]; + * + * export default CAPABILITIES; + * ``` + * + * @param capabilities Array of capability labels. + * @param outPath Destination file path for the TypeScript module. + * + * @throws {Error} If the file cannot be written or renamed. + * + * @remarks + * - Ensures the parent directory exists. + * - Writes to a temporary file first, then atomically replaces the target. + * - Uses JSON string encoding for each element to safely handle quotes + * and escape sequences. + */ +export function writeTsFile(capabilities: string[], outPath: string): void { + const outDir = path.dirname(outPath); + if (outDir) fs.mkdirSync(outDir, { recursive: true }); + + const lines = capabilities.map((cap) => ` ${JSON.stringify(cap)}`).join(",\n"); + const tsContent = + `const CAPABILITIES = [\n${lines}\n];\n\nexport default CAPABILITIES;\n`; + + const tmp = path.join( + outDir || ".", + `${path.basename(outPath)}.${process.pid}.${Date.now()}.tmp` + ); + fs.writeFileSync(tmp, tsContent, { encoding: "utf8" }); + fs.renameSync(tmp, outPath); +} + +/** + * Render capabilities as a Markdown bullet list, matching the style: + * two leading spaces, a dash, a space, then the label. + * + * @param capabilities Capabilities to render. + */ +function renderMarkdownList(capabilities: string[]): string { + return capabilities.map((c) => `- ${c}`).join("\n"); +} + +/** + * Update a Markdown file's "Valid Capabilities" section. + * + * If markers exist, replace content between: + * ... + * + * If no markers, locate the + * "## Valid Capabilities" section, then replace the bullet list between + * the line "Examples may include (but are not limited to):" and the next + * Markdown H2 heading (i.e., a line starting with "## "). + * + * @param mdPath Path to the Markdown file to update (in-place). + * @param capabilities Capability labels to inject. + * @returns A short status string describing what was updated. + * @throws {Error} If the file cannot be read or written, or section cannot be found. + */ +export function updateMarkdownCapabilities( + mdPath: string, + capabilities: string[] +): string { + let md: string; + try { + md = fs.readFileSync(mdPath, "utf8"); + } catch (e) { + throw new Error(`Error reading Markdown: ${String(e)}`); + } + + const listBlock = renderMarkdownList(capabilities); + + // 1. Explicit markers + const startMarker = ""; + const endMarker = ""; + const markerRe = new RegExp( + `${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}`, + "m" + ); + + if (markerRe.test(md)) { + const replacement = [ + startMarker, + "", + listBlock, + endMarker, + ].join("\n"); + md = md.replace(markerRe, replacement); + fs.writeFileSync(mdPath, md, "utf8"); + return "Updated Markdown between capability markers."; + } + + // 2. Find the "Valid Capabilities" section and replace bullets + const validCapHeadingRe = /^##\s+Valid\s+Capabilities\s*$/m; + const headingMatch = md.match(validCapHeadingRe); + if (!headingMatch) { + throw new Error(`Could not find "## Valid Capabilities" heading in ${mdPath}`); + } + + // Index after the heading line + const headingIdx = headingMatch.index!; + const afterHeadingIdx = headingIdx + headingMatch[0].length; + + // From after heading, find the "Examples may include..." line (optional) + const tail = md.slice(afterHeadingIdx); + const examplesLineRe = /^(?:\r?\n)+.*Examples may include.*\r?\n/m; + const examplesMatch = tail.match(examplesLineRe); + + let blockStartInTail: number; + let keepExamplesLine = false; + if (examplesMatch) { + // Start replacing after this line + blockStartInTail = examplesMatch.index! + examplesMatch[0].length; + keepExamplesLine = true; + } else { + // No examples line; start after the heading's newline(s) + const newlineAfterHeadingRe = /^(?:\r?\n)+/; + const newlineMatch = tail.match(newlineAfterHeadingRe); + blockStartInTail = newlineMatch ? (newlineMatch.index! + newlineMatch[0].length) : 0; + } + + const tailAfterStart = tail.slice(blockStartInTail); + + // Find the next H2 heading that marks end of this section + const nextH2Re = /^\s*##\s+/m; + const nextH2Match = tailAfterStart.match(nextH2Re); + const blockEndInTail = + nextH2Match ? blockStartInTail + nextH2Match.index! : tail.length; + + const before = md.slice(0, afterHeadingIdx); + const middleBeforeStart = tail.slice(0, blockStartInTail); + const after = tail.slice(blockEndInTail); + + const preface = keepExamplesLine + ? middleBeforeStart // keep the "Examples..." line as-is + : middleBeforeStart; + + // Ensure we have a leading newline before list, and a blank line after. + const injected = + preface.replace(/\s*$/, "\n") + listBlock + "\n\n"; + + const newMd = before + injected + after; + + fs.writeFileSync(mdPath, newMd, "utf8"); + return 'Updated Markdown "Valid Capabilities" bullet list.'; +} + +/** Escape a string for use in a RegExp constructor. */ +function escapeRegExp(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * Parse command-line arguments. + * + * Supported options: + * - `--ts-out `: Write a TypeScript module with the capabilities. + * - `--md `: Update the Markdown file in-place with the capabilities. + * - `--sort` / `--no-sort`: Enable or disable sorting of capability labels. + * + * @param argv Raw argument array (excluding `node`/script name). + * @returns Parsed configuration: `{ xmlPath, tsOut, md, sort }`. + * @throws {Error} On unknown options or missing arguments. + */ +function parseArgs(argv: string[]) { + const args = { + xmlPath: "", + tsOut: undefined as string | undefined, + md: undefined as string | undefined, + sort: true, + }; + const rest: string[] = []; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (a === "--ts-out") { + const v = argv[++i]; + if (!v) throw new Error("--ts-out requires a value"); + args.tsOut = v; + } else if (a === "--md") { + const v = argv[++i]; + if (!v) throw new Error("--md requires a value"); + args.md = v; + } else if (a === "--sort") { + args.sort = true; + } else if (a === "--no-sort") { + args.sort = false; + } else if (a.startsWith("--")) { + throw new Error(`Unknown option: ${a}`); + } else { + rest.push(a); + } + } + if (rest.length < 1) { + throw new Error( + "Missing XML path.\nUsage: capabilities.ts [--ts-out ] [--md ] [--sort|--no-sort]" + ); + } + args.xmlPath = rest[0]; + return args; +} + +/** + * CLI entry point. + * + * Behavior: + * - Extracts leaf capabilities from the provided XML. + * - Prints them to stdout (one per line). + * - If `--ts-out` is provided, writes the TypeScript module and reports to stderr. + * - If `--md` is provided, updates the Markdown list in-place and reports to stderr. + * + * Exit codes: + * - `0` on success. + * - Non-zero on error. + */ +function main() { + const argv = process.argv.slice(2); + try { + const { xmlPath, tsOut, md, sort } = parseArgs(argv); + const caps = extractLeafCapabilities(xmlPath, { sort }); + + // stdout: one per line (like the Python version) + for (const c of caps) process.stdout.write(`${c}\n`); + + if (tsOut) { + writeTsFile(caps, tsOut); + process.stderr.write(`\n✅ Wrote ${caps.length} capabilities to ${tsOut}\n`); + } + + if (md) { + const status = updateMarkdownCapabilities(md, caps); + process.stderr.write(`\n✅ ${status} (${md})\n`); + } + } catch (e) { + process.stderr.write(String(e) + "\n"); + process.exit(1); + } +} + +if (import.meta.url === `file://${fileURLToPath(import.meta.url)}`) { + main(); +} From 0f869cf324d6ffb5fff6326f2b61d76c09d1d21f Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Tue, 4 Nov 2025 17:26:34 +0000 Subject: [PATCH 5/8] refactor: rename files and adjust docs to match the rest --- package-lock.json | 570 ++++++++++-------- src/capabilities.ts | 38 +- ...bility.ts => hasValidLeadingCapability.ts} | 7 +- ...lities.ts => ukhsaBusinessCapabilities.ts} | 7 + 4 files changed, 346 insertions(+), 276 deletions(-) rename src/functions/{has-valid-leading-capability.ts => hasValidLeadingCapability.ts} (75%) rename src/functions/{ukhsa-business-capabilities.ts => ukhsaBusinessCapabilities.ts} (95%) diff --git a/package-lock.json b/package-lock.json index 055a3a28..ed174c0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@swc/jest": "^0.2.39", "@types/jest": "^30.0.0", "cpx2": "^8.0.0", + "fast-xml-parser": "^5.2.5", "husky": "^9.1.7", "jest": "^30.2.0", "jest-junit": "^16.0.0", @@ -70,9 +71,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", "engines": { @@ -80,21 +81,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -121,14 +123,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -227,9 +229,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "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": { @@ -261,13 +263,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -531,18 +533,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -550,14 +552,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -852,9 +854,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", "dev": true, "license": "MIT", "optional": true, @@ -864,9 +866,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", "dev": true, "license": "MIT", "optional": true, @@ -886,9 +888,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -903,9 +905,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -920,9 +922,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -937,9 +939,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -954,9 +956,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -971,9 +973,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -988,9 +990,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -1005,9 +1007,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -1022,9 +1024,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -1039,9 +1041,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -1056,9 +1058,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -1073,9 +1075,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -1090,9 +1092,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -1107,9 +1109,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -1124,9 +1126,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -1141,9 +1143,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -1158,9 +1160,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -1175,9 +1177,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", - "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -1192,9 +1194,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -1209,9 +1211,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", - "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -1226,9 +1228,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -1243,9 +1245,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", - "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], @@ -1260,9 +1262,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -1277,9 +1279,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -1294,9 +1296,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -1311,9 +1313,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -3068,9 +3070,9 @@ } }, "node_modules/@stoplight/spectral-ruleset-migrator": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.2.tgz", - "integrity": "sha512-6r5i4hrDmppspSSxdUKKNHc07NGSSIkvwKNk3M5ukCwvSslImvDEimeWAhPBryhmSJ82YAsKr8erZZpKullxWw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.3.tgz", + "integrity": "sha512-+9Y1zFxYmSsneT5FPkgS1IlRQs0VgtdMT77f5xf6vzje9ezyhfs7oXwbZOCSZjEJew8iVZBKQtiOFndcBrdtqg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3209,12 +3211,13 @@ } }, "node_modules/@swc/core": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.21.tgz", - "integrity": "sha512-umBaSb65O1v6Lt8RV3o5srw0nKr25amf/yRIGFPug63sAerL9n2UkmfGywA1l1aN81W7faXIynF0JmlQ2wPSdw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.0.tgz", + "integrity": "sha512-8SnJV+JV0rYbfSiEiUvYOmf62E7QwsEG+aZueqSlKoxFt0pw333+bgZSQXGUV6etXU88nxur0afVMaINujBMSw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" @@ -3227,16 +3230,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.21", - "@swc/core-darwin-x64": "1.13.21", - "@swc/core-linux-arm-gnueabihf": "1.13.21", - "@swc/core-linux-arm64-gnu": "1.13.21", - "@swc/core-linux-arm64-musl": "1.13.21", - "@swc/core-linux-x64-gnu": "1.13.21", - "@swc/core-linux-x64-musl": "1.13.21", - "@swc/core-win32-arm64-msvc": "1.13.21", - "@swc/core-win32-ia32-msvc": "1.13.21", - "@swc/core-win32-x64-msvc": "1.13.21" + "@swc/core-darwin-arm64": "1.15.0", + "@swc/core-darwin-x64": "1.15.0", + "@swc/core-linux-arm-gnueabihf": "1.15.0", + "@swc/core-linux-arm64-gnu": "1.15.0", + "@swc/core-linux-arm64-musl": "1.15.0", + "@swc/core-linux-x64-gnu": "1.15.0", + "@swc/core-linux-x64-musl": "1.15.0", + "@swc/core-win32-arm64-msvc": "1.15.0", + "@swc/core-win32-ia32-msvc": "1.15.0", + "@swc/core-win32-x64-msvc": "1.15.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -3248,9 +3251,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.21.tgz", - "integrity": "sha512-0jaz9r7f0PDK8OyyVooadv8dkFlQmVmBK6DtAnWSRjkCbNt4sdqsc9ZkyEDJXaxOVcMQ3pJx/Igniyw5xqACLw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.0.tgz", + "integrity": "sha512-TBKWkbnShnEjlIbO4/gfsrIgAqHBVqgPWLbWmPdZ80bF393yJcLgkrb7bZEnJs6FCbSSuGwZv2rx1jDR2zo6YA==", "cpu": [ "arm64" ], @@ -3265,9 +3268,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.21.tgz", - "integrity": "sha512-pLeZn+NTGa7oW/ysD6oM82BjKZl71WNJR9BKXRsOhrNQeUWv55DCoZT2P4DzeU5Xgjmos+iMoDLg/9R6Ngc0PA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.0.tgz", + "integrity": "sha512-f5JKL1v1H56CIZc1pVn4RGPOfnWqPwmuHdpf4wesvXunF1Bx85YgcspW5YxwqG5J9g3nPU610UFuExJXVUzOiQ==", "cpu": [ "x64" ], @@ -3282,9 +3285,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.21.tgz", - "integrity": "sha512-p9aYzTmP7qVDPkXxnbekOfbT11kxnPiuLrUbgpN/vn6sxXDCObMAiY63WlDR0IauBK571WUdmgb04goe/xTQWw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.0.tgz", + "integrity": "sha512-duK6nG+WyuunnfsfiTUQdzC9Fk8cyDLqT9zyXvY2i2YgDu5+BH5W6wM5O4mDNCU5MocyB/SuF5YDF7XySnowiQ==", "cpu": [ "arm" ], @@ -3299,9 +3302,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.21.tgz", - "integrity": "sha512-yRqFoGlCwEX1nS7OajBE23d0LPeONmFAgoe4rgRYvaUb60qGxIJoMMdvF2g3dum9ZyVDYAb3kP09hbXFbMGr4A==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.0.tgz", + "integrity": "sha512-ITe9iDtTRXM98B91rvyPP6qDVbhUBnmA/j4UxrHlMQ0RlwpqTjfZYZkD0uclOxSZ6qIrOj/X5CaoJlDUuQ0+Cw==", "cpu": [ "arm64" ], @@ -3316,9 +3319,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.21.tgz", - "integrity": "sha512-wu5EGA86gtdYMW69eU80jROzArzD3/6G6zzK0VVR+OFt/0zqbajiiszIpaniOVACObLfJEcShQ05B3q0+CpUEg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.0.tgz", + "integrity": "sha512-Q5ldc2bzriuzYEoAuqJ9Vr3FyZhakk5hiwDbniZ8tlEXpbjBhbOleGf9/gkhLaouDnkNUEazFW9mtqwUTRdh7Q==", "cpu": [ "arm64" ], @@ -3333,9 +3336,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.21.tgz", - "integrity": "sha512-AoGGVPNXH3C4S7WlJOxN1nGW5nj//J9uKysS7CIBotRmHXfHO4wPK3TVFRTA4cuouAWBBn7O8m3A99p/GR+iaw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.0.tgz", + "integrity": "sha512-pY4is+jEpOxlYCSnI+7N8Oxbap9TmTz5YT84tUvRTlOlTBwFAUlWFCX0FRwWJlsfP0TxbqhIe8dNNzlsEmJbXQ==", "cpu": [ "x64" ], @@ -3350,9 +3353,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.21.tgz", - "integrity": "sha512-cBy2amuDuxMZnEq16MqGu+DUlEFqI+7F/OACNlk7zEJKq48jJKGEMqJz3X2ucJE5jqUIg6Pos6Uo/y+vuWQymQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.0.tgz", + "integrity": "sha512-zYEt5eT8y8RUpoe7t5pjpoOdGu+/gSTExj8PV86efhj6ugB3bPlj3Y85ogdW3WMVXr4NvwqvzdaYGCZfXzSyVg==", "cpu": [ "x64" ], @@ -3367,9 +3370,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.21.tgz", - "integrity": "sha512-2xfR5gnqBGOMOlY3s1QiFTXZaivTILMwX67FD2uzT6OCbT/3lyAM/4+3BptBXD8pUkkOGMFLsdeHw4fbO1GrpQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.0.tgz", + "integrity": "sha512-zC1rmOgFH5v2BCbByOazEqs0aRNpTdLRchDExfcCfgKgeaD+IdpUOqp7i3VG1YzkcnbuZjMlXfM0ugpt+CddoA==", "cpu": [ "arm64" ], @@ -3384,9 +3387,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.21.tgz", - "integrity": "sha512-0pkpgKlBDwUImWTQxLakKbzZI6TIGVVAxk658oxrY8VK+hxRy2iezFY6m5Urmeds47M/cnW3dO+OY4C2caOF8A==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.0.tgz", + "integrity": "sha512-7t9U9KwMwQblkdJIH+zX1V4q1o3o41i0HNO+VlnAHT5o+5qHJ963PHKJ/pX3P2UlZnBCY465orJuflAN4rAP9A==", "cpu": [ "ia32" ], @@ -3401,9 +3404,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.21", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.21.tgz", - "integrity": "sha512-DAnIw2J95TOW4Kr7NBx12vlZPW3QndbpFMmuC7x+fPoozoLpEscaDkiYhk7/sTtY9pubPMfHFPBORlbqyQCfOQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.0.tgz", + "integrity": "sha512-VE0Zod5vcs8iMLT64m5QS1DlTMXJFI/qSgtMDRx8rtZrnjt6/9NW8XUaiPJuRu8GluEO1hmHoyf1qlbY19gGSQ==", "cpu": [ "x64" ], @@ -3554,9 +3557,9 @@ } }, "node_modules/@types/conventional-commits-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", - "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz", + "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==", "dev": true, "license": "MIT", "dependencies": { @@ -3685,12 +3688,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.0.tgz", - "integrity": "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "license": "MIT", + "peer": true, "dependencies": { - "undici-types": "~7.14.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/sarif": { @@ -3729,15 +3733,15 @@ "license": "MIT" }, "node_modules/@types/urijs": { - "version": "1.19.25", - "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", - "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", + "version": "1.19.26", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.26.tgz", + "integrity": "sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg==", "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", "dev": true, "license": "MIT", "dependencies": { @@ -4080,6 +4084,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4448,9 +4453,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.13.tgz", - "integrity": "sha512-7s16KR8io8nIBWQyCYhmFhd+ebIzb9VKTzki+wOJXHTxTnV6+mFGH3+Jwn1zoKaY9/H9T/0BcKCZnzXljPnpSQ==", + "version": "2.8.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.24.tgz", + "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4494,9 +4499,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", - "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", "dev": true, "funding": [ { @@ -4513,12 +4518,13 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.9", - "caniuse-lite": "^1.0.30001746", - "electron-to-chromium": "^1.5.227", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -4632,9 +4638,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001749", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz", - "integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==", + "version": "1.0.30001753", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", + "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", "dev": true, "funding": [ { @@ -4813,9 +4819,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, "license": "MIT" }, @@ -4943,9 +4949,9 @@ "license": "MIT" }, "node_modules/core-js": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", - "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz", + "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4960,6 +4966,7 @@ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -4982,13 +4989,13 @@ } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", - "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", + "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", "dev": true, "license": "MIT", "dependencies": { - "jiti": "^2.4.1" + "jiti": "^2.6.1" }, "engines": { "node": ">=v18" @@ -5079,11 +5086,11 @@ } }, "node_modules/cpx2/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -5403,9 +5410,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.233", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.233.tgz", - "integrity": "sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==", + "version": "1.5.244", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", + "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", "dev": true, "license": "ISC" }, @@ -5609,9 +5616,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5622,32 +5629,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { @@ -5814,6 +5821,25 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-xml-parser": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.1.tgz", + "integrity": "sha512-jbNkWiv2Ec1A7wuuxk0br0d0aTMUtQ4IkL+l/i1r9PRf6pLXjDgsBsWwO+UyczmQlnehi4Tbc8/KIvxGQe+I/A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -6133,9 +6159,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.11.0.tgz", - "integrity": "sha512-sNsqf7XKQ38IawiVGPOoAlqZo1DMrO7TU+ZcZwi7yLl7/7S0JwmoBMKz/IkUPhSoXM0Ng3vT0yB1iCe5XavDeQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7196,6 +7222,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -8390,6 +8417,7 @@ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -9850,9 +9878,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", - "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, @@ -11374,13 +11402,13 @@ } }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -11454,6 +11482,7 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -12089,6 +12118,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -12299,6 +12341,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12473,6 +12516,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12514,9 +12558,9 @@ } }, "node_modules/undici-types": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", - "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, "node_modules/unicorn-magic": { @@ -12638,9 +12682,9 @@ } }, "node_modules/unified-engine/node_modules/@types/node": { - "version": "22.18.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.8.tgz", - "integrity": "sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==", + "version": "22.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", + "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", "dev": true, "license": "MIT", "dependencies": { @@ -12807,9 +12851,9 @@ } }, "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "dev": true, "license": "MIT", "dependencies": { @@ -12880,9 +12924,9 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12940,9 +12984,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, "funding": [ { @@ -13125,9 +13169,9 @@ } }, "node_modules/vfile-reporter/node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, diff --git a/src/capabilities.ts b/src/capabilities.ts index 5c058159..c337337c 100644 --- a/src/capabilities.ts +++ b/src/capabilities.ts @@ -38,8 +38,8 @@ type ExtractOptions = { * no other BusinessCapability’s `` references this object's id * via its `"parent"` attribute). * - * @param xmlPath Absolute or relative path to the diagrams.net XML file. - * @param opts Options controlling extraction behavior. + * @param xmlPath - Absolute or relative path to the diagrams.net XML file. + * @param opts - Options controlling extraction behavior. * @returns Array of unique capability labels. Empty labels are ignored. * Duplicates (case-sensitive) are removed. * @throws {Error} If the file cannot be read or the XML cannot be parsed. @@ -157,8 +157,8 @@ export function extractLeafCapabilities( * export default CAPABILITIES; * ``` * - * @param capabilities Array of capability labels. - * @param outPath Destination file path for the TypeScript module. + * @param capabilities - Array of capability labels. + * @param outPath - Destination file path for the TypeScript module. * * @throws {Error} If the file cannot be written or renamed. * @@ -173,8 +173,17 @@ export function writeTsFile(capabilities: string[], outPath: string): void { if (outDir) fs.mkdirSync(outDir, { recursive: true }); const lines = capabilities.map((cap) => ` ${JSON.stringify(cap)}`).join(",\n"); + const headerComment = [ + "/**", + " * Enumerates the UKHSA Business Capabilities accepted by validation rules.", + " *", + " * @remarks", + " * Generated via the `capabilities.ts` script to keep the controlled list in sync", + " * with source taxonomy data.", + " */", + ].join("\n"); const tsContent = - `const CAPABILITIES = [\n${lines}\n];\n\nexport default CAPABILITIES;\n`; + `${headerComment}\nconst CAPABILITIES = [\n${lines}\n];\n\nexport default CAPABILITIES;\n`; const tmp = path.join( outDir || ".", @@ -185,10 +194,10 @@ export function writeTsFile(capabilities: string[], outPath: string): void { } /** - * Render capabilities as a Markdown bullet list, matching the style: - * two leading spaces, a dash, a space, then the label. + * Render capabilities as a Markdown bullet list, one label per line. * - * @param capabilities Capabilities to render. + * @param capabilities - Capability labels to render. + * @returns Markdown bullet list comprised of the provided capabilities. */ function renderMarkdownList(capabilities: string[]): string { return capabilities.map((c) => `- ${c}`).join("\n"); @@ -205,8 +214,8 @@ function renderMarkdownList(capabilities: string[]): string { * the line "Examples may include (but are not limited to):" and the next * Markdown H2 heading (i.e., a line starting with "## "). * - * @param mdPath Path to the Markdown file to update (in-place). - * @param capabilities Capability labels to inject. + * @param mdPath - Path to the Markdown file to update (in-place). + * @param capabilities - Capability labels to inject. * @returns A short status string describing what was updated. * @throws {Error} If the file cannot be read or written, or section cannot be found. */ @@ -298,7 +307,12 @@ export function updateMarkdownCapabilities( return 'Updated Markdown "Valid Capabilities" bullet list.'; } -/** Escape a string for use in a RegExp constructor. */ +/** + * Escape a string for safe inclusion in a regular expression. + * + * @param s - String to escape. + * @returns The escaped representation of the input string. + */ function escapeRegExp(s: string): string { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } @@ -311,7 +325,7 @@ function escapeRegExp(s: string): string { * - `--md `: Update the Markdown file in-place with the capabilities. * - `--sort` / `--no-sort`: Enable or disable sorting of capability labels. * - * @param argv Raw argument array (excluding `node`/script name). + * @param argv - Raw argument array (excluding `node`/script name). * @returns Parsed configuration: `{ xmlPath, tsOut, md, sort }`. * @throws {Error} On unknown options or missing arguments. */ diff --git a/src/functions/has-valid-leading-capability.ts b/src/functions/hasValidLeadingCapability.ts similarity index 75% rename from src/functions/has-valid-leading-capability.ts rename to src/functions/hasValidLeadingCapability.ts index 463128ea..4f306333 100644 --- a/src/functions/has-valid-leading-capability.ts +++ b/src/functions/hasValidLeadingCapability.ts @@ -21,7 +21,12 @@ import type { import CAPABILITIES from './ukhsa-business-capabilities'; /** - * Spectral custom function to validate `info.x-leading-capability` field. + * Spectral custom function to validate the `info.x-leading-capability` field. + * + * @param targetVal - The value supplied by the Spectral rule selector. + * @param opts - Unused options placeholder required by the Spectral function signature. + * @param context - Spectral ruleset execution context. + * @returns Rule results describing validation issues, or an empty array when valid. */ const validateLeadingCapability = function ( targetVal: string | null | undefined, diff --git a/src/functions/ukhsa-business-capabilities.ts b/src/functions/ukhsaBusinessCapabilities.ts similarity index 95% rename from src/functions/ukhsa-business-capabilities.ts rename to src/functions/ukhsaBusinessCapabilities.ts index 60033f1f..9575b1d8 100644 --- a/src/functions/ukhsa-business-capabilities.ts +++ b/src/functions/ukhsaBusinessCapabilities.ts @@ -1,3 +1,10 @@ +/** + * Enumerates the UKHSA Business Capabilities accepted by validation rules. + * + * @remarks + * Generated via the `capabilities.ts` script to keep the controlled list in sync + * with source taxonomy data. + */ const CAPABILITIES = [ "Advice Management", "Behavioural Science", From e7faa997ee90f956de9b3adeb6bd18f1ecb77c60 Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Tue, 4 Nov 2025 17:29:44 +0000 Subject: [PATCH 6/8] fix: business capability check --- src/functions/hasValidLeadingCapability.ts | 2 +- ukhsa.oas.rules.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/functions/hasValidLeadingCapability.ts b/src/functions/hasValidLeadingCapability.ts index 4f306333..beb348b2 100644 --- a/src/functions/hasValidLeadingCapability.ts +++ b/src/functions/hasValidLeadingCapability.ts @@ -18,7 +18,7 @@ import type { RulesetFunctionContext, } from '@stoplight/spectral-core'; -import CAPABILITIES from './ukhsa-business-capabilities'; +import CAPABILITIES from './ukhsaBusinessCapabilities.js'; /** * Spectral custom function to validate the `info.x-leading-capability` field. diff --git a/ukhsa.oas.rules.yml b/ukhsa.oas.rules.yml index 1dfc9d96..47ac7c7a 100644 --- a/ukhsa.oas.rules.yml +++ b/ukhsa.oas.rules.yml @@ -9,7 +9,7 @@ functions: - isApiInfoJsonSchema - hasRequiredProblemDetailsErrorResponses - overrideSeverity - - has-valid-leading-capability + - hasValidLeadingCapability functionsDir: dist/functions @@ -368,6 +368,6 @@ rules: severity: warn given: $.info then: - - function: has-valid-leading-capability + - function: hasValidLeadingCapability field: x-leading-capability recommended: true From d8fa4f05ea2fe4bf490ccd6d32de3d351e6e8d4c Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Wed, 5 Nov 2025 12:09:36 +0000 Subject: [PATCH 7/8] test: add tests for leading capabilities --- .../hasValidLeadingCapability.test.ts | 44 +++++++++++++++ .../must-have-info-leading-capability.test.ts | 54 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/__tests__/functions/hasValidLeadingCapability.test.ts create mode 100644 src/__tests__/rules/must-have-info-leading-capability.test.ts diff --git a/src/__tests__/functions/hasValidLeadingCapability.test.ts b/src/__tests__/functions/hasValidLeadingCapability.test.ts new file mode 100644 index 00000000..8fd24250 --- /dev/null +++ b/src/__tests__/functions/hasValidLeadingCapability.test.ts @@ -0,0 +1,44 @@ +import type { RulesetFunctionContext } from '@stoplight/spectral-core'; +import hasValidLeadingCapability from '../../functions/hasValidLeadingCapability.js'; + +const createContext = (path: (string | number)[]): RulesetFunctionContext => + ({ + document: {} as any, + documentInventory: {} as any, + rule: {} as any, + path, + }) as unknown as RulesetFunctionContext; + +describe('hasValidLeadingCapability', () => { + it('returns no results when the capability exists in the controlled list', () => { + const context = createContext(['info', 'x-leading-capability']); + const result = hasValidLeadingCapability('Advice Management', {}, context); + + expect(result).toEqual([]); + }); + + it('returns an error when the capability is not recognised', () => { + const context = createContext(['info', 'x-leading-capability']); + const result = hasValidLeadingCapability('Unknown Capability', {}, context); + + expect(result).toEqual([ + { + message: + "Missing or wrong 'info.x-leading-capability', should be a valid UKHSA Business Capability.", + path: ['info', 'x-leading-capability'], + }, + ]); + }); + + it('returns an error when the value is not a string', () => { + const context = createContext(['info', 'x-leading-capability']); + const result = hasValidLeadingCapability(undefined, {}, context); + + expect(result).toEqual([ + { + message: '`x-leading-capability` must be a string.', + path: ['info', 'x-leading-capability'], + }, + ]); + }); +}); diff --git a/src/__tests__/rules/must-have-info-leading-capability.test.ts b/src/__tests__/rules/must-have-info-leading-capability.test.ts new file mode 100644 index 00000000..33d9b73a --- /dev/null +++ b/src/__tests__/rules/must-have-info-leading-capability.test.ts @@ -0,0 +1,54 @@ +import { DiagnosticSeverity } from '@stoplight/types'; +import testRule from '../__helpers__/helper.mjs'; + +const invalidMessage = "Missing or wrong `info.x-leading-capability`, should be a valid UKHSA Business Capability."; + +testRule('must-have-info-leading-capability', [ + { + name: 'valid: info.x-leading-capability matches a known capability', + document: ` +openapi: 3.1.0 +info: + title: Sample API + version: 1.0.0 + x-leading-capability: Advice Management +paths: {} +`, + errors: [], + }, + { + name: 'invalid: info.x-leading-capability missing', + document: ` +openapi: 3.1.0 +info: + title: Sample API + version: 1.0.0 +paths: {} +`, + errors: [ + { + severity: DiagnosticSeverity.Warning, + path: ['info'], + message: invalidMessage, + }, + ], + }, + { + name: 'invalid: info.x-leading-capability not in controlled list', + document: ` +openapi: 3.1.0 +info: + title: Sample API + version: 1.0.0 + x-leading-capability: Not A Real Capability +paths: {} +`, + errors: [ + { + severity: DiagnosticSeverity.Warning, + path: ['info', 'x-leading-capability'], + message: invalidMessage, + }, + ], + }, +]); From ca4792537c9202a1faf2e016ca128f096836d7b6 Mon Sep 17 00:00:00 2001 From: Andrii Iudin Date: Wed, 5 Nov 2025 12:12:03 +0000 Subject: [PATCH 8/8] docs: correct name in the example --- src/capabilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/capabilities.ts b/src/capabilities.ts index c337337c..3085c3e3 100644 --- a/src/capabilities.ts +++ b/src/capabilities.ts @@ -6,7 +6,7 @@ * * Example CLI usage: * npx ts-node src/capabilities.ts path/to/capabilities.xml \ - * --ts-out src/functions/ukhsa-business-capabilities.ts \ + * --ts-out src/functions/ukhsaBusinessCapabilities.ts \ * --md docs/must-have-info-leading-capability.md */