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..68761352 --- /dev/null +++ b/docs/spectral-rules/must/must-have-info-leading-capability.md @@ -0,0 +1,182 @@ +# **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): + + + +- 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 + +```yaml +info: + title: Test Results API + x-leading-capability: Border Management +``` + +## Invalid Example + +```yaml +info: + title: Test Results API + x-leading-capability: Random Innovation +``` 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/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/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/__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, + }, + ], + }, +]); diff --git a/src/capabilities.ts b/src/capabilities.ts new file mode 100644 index 00000000..3085c3e3 --- /dev/null +++ b/src/capabilities.ts @@ -0,0 +1,408 @@ +#!/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/ukhsaBusinessCapabilities.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 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 = + `${headerComment}\nconst 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, one label per line. + * + * @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"); +} + +/** + * 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 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, "\\$&"); +} + +/** + * 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(); +} diff --git a/src/functions/hasValidLeadingCapability.ts b/src/functions/hasValidLeadingCapability.ts new file mode 100644 index 00000000..beb348b2 --- /dev/null +++ b/src/functions/hasValidLeadingCapability.ts @@ -0,0 +1,58 @@ +'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 './ukhsaBusinessCapabilities.js'; + +/** + * 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, + 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/ukhsaBusinessCapabilities.ts b/src/functions/ukhsaBusinessCapabilities.ts new file mode 100644 index 00000000..9575b1d8 --- /dev/null +++ b/src/functions/ukhsaBusinessCapabilities.ts @@ -0,0 +1,164 @@ +/** + * 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", + "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" +]; + +export default CAPABILITIES; diff --git a/ukhsa.oas.rules.yml b/ukhsa.oas.rules.yml index b8257ecd..47ac7c7a 100644 --- a/ukhsa.oas.rules.yml +++ b/ukhsa.oas.rules.yml @@ -9,6 +9,7 @@ functions: - isApiInfoJsonSchema - hasRequiredProblemDetailsErrorResponses - overrideSeverity + - hasValidLeadingCapability 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: warn + given: $.info + then: + - function: hasValidLeadingCapability + field: x-leading-capability + recommended: true