From e17a24cf0e2cb06fe56ed2a2dbcdc7a868287016 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Mon, 24 Nov 2025 16:49:44 +0100 Subject: [PATCH 01/14] chore: initial subpackage setup --- components/conformance/package.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 components/conformance/package.json diff --git a/components/conformance/package.json b/components/conformance/package.json new file mode 100644 index 0000000..7a546e3 --- /dev/null +++ b/components/conformance/package.json @@ -0,0 +1,13 @@ +{ + "name": "conformance", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.9.0" +} From adec3a1ea2f5741e26e2920eefd5656496c56d33 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Tue, 25 Nov 2025 15:54:58 +0100 Subject: [PATCH 02/14] - Add README.md to discuss - Update license to AGPL-3.0, per reference server --- components/conformance/README.md | 289 ++++++++++++++++++++++++++++ components/conformance/package.json | 4 +- 2 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 components/conformance/README.md diff --git a/components/conformance/README.md b/components/conformance/README.md new file mode 100644 index 0000000..717b8ca --- /dev/null +++ b/components/conformance/README.md @@ -0,0 +1,289 @@ +# FIRES Conformance Test Suite + +[![AGPL License](https://img.shields.io/badge/license-AGPL-blue.svg)](http://www.gnu.org/licenses/agpl-3.0) + +A comprehensive conformance test suite for validating FIRES protocol implementations. This tool helps ensure that FIRES servers correctly implement the protocol for exchanging moderation recommendations about instances that may be sharing harmful content. + +## What is FIRES? + +**FIRES** stands for **F**ediverse **I**ntelligence **R**eplication **E**ndpoint **S**erver. It's a protocol that allows Fediverse instances to exchange lists of recommended actions to take against instances sharing nonconsensual imagery, abuse, or bigotry. + +This conformance suite validates that server implementations correctly follow the [FIRES Protocol Specification](https://fires.fedimod.org/reference/protocol/). + +## Features + +- Protocol-level validation of FIRES server implementations +- Support for both local and remote server testing +- Multiple output formats for CI/CD integration +- Docker-based distribution for platform independence +- Selective test execution by category +- Built on Vitest for fast, reliable testing + +## Installation + +### Via npm/pnpm/yarn + +```bash +# Using npx (no installation required) +npx @fedimod/fires-conformance --url https://your-fires-server.example + +# Using pnpm +pnpm dlx @fedimod/fires-conformance --url https://your-fires-server.example + +# Using yarn +yarn dlx @fedimod/fires-conformance --url https://your-fires-server.example + +# Global installation +npm install -g @fedimod/fires-conformance +fires-conformance --url https://your-fires-server.example +``` + +### Via Docker + +```bash +# Run conformance tests against a server +docker run --rm ghcr.io/fedimod/fires-conformance \ + --url https://your-fires-server.example + +# Run tests with specific output format +docker run --rm ghcr.io/fedimod/fires-conformance \ + --url https://your-fires-server.example \ + --reporter junit \ + --output-file results.xml +``` + +## Usage + +### Basic Usage + +Test a FIRES server implementation: + +```bash +fires-conformance --url https://fires.example.org +``` + +### Command Line Options + +#### Required Options + +- `--url ` - URL of the FIRES server to test + +#### Output Options + +- `--reporter ` - Output format for test results + - `console` (default) - Human-readable console output + - `junit` - JUnit XML format for CI/CD systems + - `html` - HTML report file + - `json` - JSON format for programmatic consumption + +- `--output-file ` - Path to write output file (for non-console reporters) + +#### Test Selection + +- `--category ` - Run only specific test categories (comma-separated) + - Example: `--category labels,nodeinfo` + - Available categories: + - `labels` - Label endpoint tests + - `datasets` - Dataset endpoint tests (when implemented) + - `nodeinfo` - NodeInfo endpoint tests + - `json-ld` - JSON-LD format validation across all endpoints + - `http` - HTTP protocol compliance + - `security` - Security-related tests + +- `--exclude-category ` - Exclude specific test categories (comma-separated) + +#### Other Options + +- `--help` - Display help information +- `--version` - Display version information +- `--verbose` - Enable verbose output +- `--no-color` - Disable colored output + +### Examples + +#### CI/CD Integration + +```bash +# Generate JUnit report for CI systems +fires-conformance \ + --url https://staging.fires.example.org \ + --reporter junit \ + --output-file test-results.xml +``` + +#### Selective Testing + +```bash +# Test only Labels endpoints +fires-conformance --url https://fires.example.org --category labels + +# Test everything except security tests +fires-conformance --url https://fires.example.org --exclude-category security + +# Test JSON-LD and HTTP compliance +fires-conformance --url https://fires.example.org --category json-ld,http +``` + +#### Docker Examples + +```bash +# Test local development server +docker run --rm --network host \ + ghcr.io/fedimod/fires-conformance \ + --url http://localhost:3333 + +# Generate JUnit report for CI +docker run --rm -v $(pwd):/output \ + ghcr.io/fedimod/fires-conformance \ + --url https://fires.example.org \ + --reporter junit \ + --output-file /output/results.xml + +# Test with verbose output +docker run --rm \ + ghcr.io/fedimod/fires-conformance \ + --url https://fires.example.org \ + --verbose +``` + +## What Gets Tested + +The conformance suite validates: + +### Labels Endpoint +- Collection endpoint (`/labels`) returns valid JSON-LD +- Individual label endpoints (`/labels/:id`) return valid JSON-LD +- Content negotiation (HTML vs JSON-LD based on Accept header) +- Pagination behavior +- Label structure and required fields +- Linked data context validity + +### NodeInfo Endpoint +- Well-known discovery endpoint (`/.well-known/nodeinfo`) +- NodeInfo 2.1 endpoint structure +- Required metadata fields +- Protocol identification + +### Datasets Endpoint (when implemented) +- Dataset collection endpoints +- Individual dataset retrieval +- Resumable data transfer +- Change tracking + +### HTTP Compliance +- Appropriate status codes +- Content-Type headers +- CORS headers (if applicable) +- Rate limiting behavior + +### Security +- HTTPS availability (for production servers) +- No sensitive data leakage +- Proper authentication rejection + +## CI/CD Integration + +### GitHub Actions + +```yaml +name: FIRES Conformance +on: [push, pull_request] + +jobs: + conformance: + runs-on: ubuntu-latest + steps: + - name: Start FIRES server + run: | + # Your server startup logic here + docker compose up -d + + - name: Wait for server + run: | + timeout 60 bash -c 'until curl -f http://localhost:3333/nodeinfo/2.1; do sleep 2; done' + + - name: Run conformance tests + run: | + npx @fedimod/fires-conformance \ + --url http://localhost:3333 \ + --reporter junit \ + --output-file results.xml + + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: results.xml +``` + +## Development + +### Running Tests Locally + +```bash +# Install dependencies +pnpm install + +# Run tests against a local server +pnpm test -- --url http://localhost:3333 + +# Run tests with coverage +pnpm test:coverage + +# Run in watch mode during development +pnpm test:watch +``` + +### Project Structure + +Tests are organized by category in separate directories for selective execution: + +``` +components/conformance/ +├── src/ +│ ├── tests/ +│ │ ├── labels/ # Label endpoint tests (--category labels) +│ │ ├── datasets/ # Dataset endpoint tests (--category datasets) +│ │ ├── nodeinfo/ # NodeInfo tests (--category nodeinfo) +│ │ ├── json-ld/ # JSON-LD validation (--category json-ld) +│ │ ├── http/ # HTTP compliance (--category http) +│ │ └── security/ # Security tests (--category security) +│ └── cli.ts # CLI interface +├── package.json +├── vitest.config.ts +├── Dockerfile +└── README.md +``` + +The CLI maps `--category` options to specific test directories, allowing Vitest to run only the relevant test files. + +## API Design Stability + +The conformance suite exposes a deliberately limited CLI interface to maintain API stability. While the test suite is built on Vitest, we don't expose all Vitest options directly to avoid locking ourselves into Vitest-specific features as part of the public API. + +If you need additional testing capabilities not covered by the current CLI options, please [open an issue](https://github.com/fedimod/fires/issues) to discuss your use case. + +## Contributing + +Contributions are welcome! When adding new tests: + +1. Place tests in the appropriate category directory for selective execution +2. Follow existing test structure and naming conventions +3. Document any new command-line options +4. Update this README with examples +5. Ensure tests work in both npm and Docker environments + +## License + +This project is licensed under the AGPL-3.0 License. + +## Acknowledgements + +[NLNet](http://nlnet.nl)     +[NGI Zero](http://nlnet.nl/NGI0) + +This project was funded through the NGI0 Entrust Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101069594. +


+[Nivenly Foundation](http://nivenly.org) + +The writing of the proposal outlining FIRES was funded by Nivenly Foundation. diff --git a/components/conformance/package.json b/components/conformance/package.json index 7a546e3..6f497b9 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -1,5 +1,5 @@ { - "name": "conformance", + "name": "@fedimod/fires-conformance", "version": "1.0.0", "description": "", "main": "index.js", @@ -8,6 +8,6 @@ }, "keywords": [], "author": "", - "license": "ISC", + "license": "AGPL-3.0", "packageManager": "pnpm@10.9.0" } From 4232924012fa97bfc3487ba87eedff240ad1d02c Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Wed, 26 Nov 2025 09:50:24 +0100 Subject: [PATCH 03/14] Apply suggested fixes Co-authored-by: Emelia Smith Signed-off-by: Steven Langbroek --- components/conformance/README.md | 7 ++----- components/conformance/package.json | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/components/conformance/README.md b/components/conformance/README.md index 717b8ca..baac053 100644 --- a/components/conformance/README.md +++ b/components/conformance/README.md @@ -2,7 +2,7 @@ [![AGPL License](https://img.shields.io/badge/license-AGPL-blue.svg)](http://www.gnu.org/licenses/agpl-3.0) -A comprehensive conformance test suite for validating FIRES protocol implementations. This tool helps ensure that FIRES servers correctly implement the protocol for exchanging moderation recommendations about instances that may be sharing harmful content. +A comprehensive conformance test suite for validating FIRES protocol implementations. This tool helps ensure that FIRES servers correctly implement the protocol for exchanging moderation data. ## What is FIRES? @@ -33,9 +33,6 @@ pnpm dlx @fedimod/fires-conformance --url https://your-fires-server.example # Using yarn yarn dlx @fedimod/fires-conformance --url https://your-fires-server.example -# Global installation -npm install -g @fedimod/fires-conformance -fires-conformance --url https://your-fires-server.example ``` ### Via Docker @@ -164,7 +161,7 @@ The conformance suite validates: - Required metadata fields - Protocol identification -### Datasets Endpoint (when implemented) +### Datasets Endpoint - Dataset collection endpoints - Individual dataset retrieval - Resumable data transfer diff --git a/components/conformance/package.json b/components/conformance/package.json index 6f497b9..e941242 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -1,6 +1,6 @@ { "name": "@fedimod/fires-conformance", - "version": "1.0.0", + "version": "0.0.0", "description": "", "main": "index.js", "scripts": { From 27d4b571c0106c76d0bb6f3343f1a1dbac5fdceb Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Wed, 26 Nov 2025 10:03:54 +0100 Subject: [PATCH 04/14] Cleanup based on feedback: - Remove npm mention until publishing has been figured out by maintainers - Rename category to suite - Remove mention of unnecessary test suites --- components/conformance/README.md | 83 ++++++++------------------------ 1 file changed, 21 insertions(+), 62 deletions(-) diff --git a/components/conformance/README.md b/components/conformance/README.md index baac053..9437829 100644 --- a/components/conformance/README.md +++ b/components/conformance/README.md @@ -16,25 +16,11 @@ This conformance suite validates that server implementations correctly follow th - Support for both local and remote server testing - Multiple output formats for CI/CD integration - Docker-based distribution for platform independence -- Selective test execution by category +- Selective test execution by suite - Built on Vitest for fast, reliable testing ## Installation -### Via npm/pnpm/yarn - -```bash -# Using npx (no installation required) -npx @fedimod/fires-conformance --url https://your-fires-server.example - -# Using pnpm -pnpm dlx @fedimod/fires-conformance --url https://your-fires-server.example - -# Using yarn -yarn dlx @fedimod/fires-conformance --url https://your-fires-server.example - -``` - ### Via Docker ```bash @@ -56,7 +42,7 @@ docker run --rm ghcr.io/fedimod/fires-conformance \ Test a FIRES server implementation: ```bash -fires-conformance --url https://fires.example.org +docker run --rm ghcr.io/fedimod/fires-conformance --url https://fires.example.org ``` ### Command Line Options @@ -77,17 +63,12 @@ fires-conformance --url https://fires.example.org #### Test Selection -- `--category ` - Run only specific test categories (comma-separated) - - Example: `--category labels,nodeinfo` - - Available categories: +- `--suites ` - Run only specific test suites (comma-separated) + - Example: `--suites labels,nodeinfo` + - Available suites: - `labels` - Label endpoint tests - `datasets` - Dataset endpoint tests (when implemented) - `nodeinfo` - NodeInfo endpoint tests - - `json-ld` - JSON-LD format validation across all endpoints - - `http` - HTTP protocol compliance - - `security` - Security-related tests - -- `--exclude-category ` - Exclude specific test categories (comma-separated) #### Other Options @@ -102,7 +83,7 @@ fires-conformance --url https://fires.example.org ```bash # Generate JUnit report for CI systems -fires-conformance \ +docker run --rm ghcr.io/fedimod/fires-conformance \ --url https://staging.fires.example.org \ --reporter junit \ --output-file test-results.xml @@ -112,13 +93,12 @@ fires-conformance \ ```bash # Test only Labels endpoints -fires-conformance --url https://fires.example.org --category labels - -# Test everything except security tests -fires-conformance --url https://fires.example.org --exclude-category security +docker run --rm ghcr.io/fedimod/fires-conformance \ + --url https://fires.example.org --suites labels -# Test JSON-LD and HTTP compliance -fires-conformance --url https://fires.example.org --category json-ld,http +# Test multiple suites +docker run --rm ghcr.io/fedimod/fires-conformance \ + --url https://fires.example.org --suites labels,nodeinfo ``` #### Docker Examples @@ -150,7 +130,6 @@ The conformance suite validates: ### Labels Endpoint - Collection endpoint (`/labels`) returns valid JSON-LD - Individual label endpoints (`/labels/:id`) return valid JSON-LD -- Content negotiation (HTML vs JSON-LD based on Accept header) - Pagination behavior - Label structure and required fields - Linked data context validity @@ -167,17 +146,6 @@ The conformance suite validates: - Resumable data transfer - Change tracking -### HTTP Compliance -- Appropriate status codes -- Content-Type headers -- CORS headers (if applicable) -- Rate limiting behavior - -### Security -- HTTPS availability (for production servers) -- No sensitive data leakage -- Proper authentication rejection - ## CI/CD Integration ### GitHub Actions @@ -201,7 +169,8 @@ jobs: - name: Run conformance tests run: | - npx @fedimod/fires-conformance \ + docker run --rm --network host \ + ghcr.io/fedimod/fires-conformance \ --url http://localhost:3333 \ --reporter junit \ --output-file results.xml @@ -233,18 +202,15 @@ pnpm test:watch ### Project Structure -Tests are organized by category in separate directories for selective execution: +Tests are organized by suite in separate directories for selective execution: ``` components/conformance/ ├── src/ │ ├── tests/ -│ │ ├── labels/ # Label endpoint tests (--category labels) -│ │ ├── datasets/ # Dataset endpoint tests (--category datasets) -│ │ ├── nodeinfo/ # NodeInfo tests (--category nodeinfo) -│ │ ├── json-ld/ # JSON-LD validation (--category json-ld) -│ │ ├── http/ # HTTP compliance (--category http) -│ │ └── security/ # Security tests (--category security) +│ │ ├── labels/ # Label endpoint tests (--suites labels) +│ │ ├── datasets/ # Dataset endpoint tests (--suites datasets) +│ │ └── nodeinfo/ # NodeInfo tests (--suites nodeinfo) │ └── cli.ts # CLI interface ├── package.json ├── vitest.config.ts @@ -252,7 +218,7 @@ components/conformance/ └── README.md ``` -The CLI maps `--category` options to specific test directories, allowing Vitest to run only the relevant test files. +The CLI maps `--suites` options to specific test directories, allowing Vitest to run only the relevant test files. ## API Design Stability @@ -264,11 +230,11 @@ If you need additional testing capabilities not covered by the current CLI optio Contributions are welcome! When adding new tests: -1. Place tests in the appropriate category directory for selective execution +1. Place tests in the appropriate suite directory for selective execution 2. Follow existing test structure and naming conventions 3. Document any new command-line options 4. Update this README with examples -5. Ensure tests work in both npm and Docker environments +5. Ensure tests work in Docker environments ## License @@ -276,11 +242,4 @@ This project is licensed under the AGPL-3.0 License. ## Acknowledgements -[NLNet](http://nlnet.nl)     -[NGI Zero](http://nlnet.nl/NGI0) - -This project was funded through the NGI0 Entrust Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101069594. -


-[Nivenly Foundation](http://nivenly.org) - -The writing of the proposal outlining FIRES was funded by Nivenly Foundation. +See [Acknowledgements](/README.md#acknowledgements) in the main FIRES repository. From 03ce6761bd124750e281954c5838a95c419d4658 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Wed, 26 Nov 2025 10:17:56 +0100 Subject: [PATCH 05/14] Add conformance test dependencies --- components/conformance/package.json | 8 +- pnpm-lock.yaml | 256 ++++++++++++++++++++++++++-- 2 files changed, 252 insertions(+), 12 deletions(-) diff --git a/components/conformance/package.json b/components/conformance/package.json index e941242..82a4dda 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -9,5 +9,11 @@ "keywords": [], "author": "", "license": "AGPL-3.0", - "packageManager": "pnpm@10.9.0" + "packageManager": "pnpm@10.9.0", + "dependencies": { + "optique": "^0.1.0", + "ts-node-maintained": "^10.9.6", + "typescript": "~5.7.3", + "vitest": "^4.0.14" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5d8c3a..7a80766 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,21 @@ importers: specifier: ^6.1.2 version: 6.1.2 + components/conformance: + dependencies: + optique: + specifier: ^0.1.0 + version: 0.1.0 + ts-node-maintained: + specifier: ^10.9.6 + version: 10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3) + typescript: + specifier: ~5.7.3 + version: 5.7.3 + vitest: + specifier: ^4.0.14 + version: 4.0.14(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + components/fires-server: dependencies: '@adonisjs/auth': @@ -1091,6 +1106,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -1783,6 +1801,35 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 + '@vitest/expect@4.0.14': + resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + + '@vitest/mocker@4.0.14': + resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.14': + resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + + '@vitest/runner@4.0.14': + resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + + '@vitest/snapshot@4.0.14': + resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + + '@vitest/spy@4.0.14': + resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + + '@vitest/utils@4.0.14': + resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} @@ -1891,11 +1938,6 @@ packages: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -2141,6 +2183,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2542,6 +2588,9 @@ packages: es-module-lexer@1.6.0: resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2650,6 +2699,9 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2673,6 +2725,10 @@ packages: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -3259,6 +3315,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -3467,6 +3526,9 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -3489,6 +3551,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + optique@0.1.0: + resolution: {integrity: sha512-1tnLA5lrcqr5vOUupcorEzLsU2OwhTnArtb2WmjPA+QO4IZjJYX0ESyZw5Nskq0K3wzlo478mclMFb5z4pNA1g==} + outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -3599,6 +3664,9 @@ packages: resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} engines: {node: '>=18'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} @@ -4100,6 +4168,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -4182,6 +4253,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stacktracey@2.1.8: resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} @@ -4189,6 +4263,9 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + streamx@2.23.0: resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} @@ -4341,6 +4418,9 @@ packages: timekeeper@2.3.1: resolution: {integrity: sha512-LeQRS7/4JcC0PgdSFnfUiStQEdiuySlCj/5SJ18D+T1n9BoY7PxKFfCwLulpHXoLUFr67HxBddQdEX47lDGx1g==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -4351,6 +4431,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tmp-cache@1.1.0: resolution: {integrity: sha512-j040fkL/x+XAZQ9K3bKGEPwgYhOZNBQLa3NXEADUiuno9C+3N2JJA4bVPDREixp604G3/vTXWA3DIPpA9lu1RQ==} engines: {node: '>=6'} @@ -4591,6 +4675,40 @@ packages: postcss: optional: true + vitest@4.0.14: + resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.14 + '@vitest/browser-preview': 4.0.14 + '@vitest/browser-webdriverio': 4.0.14 + '@vitest/ui': 4.0.14 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vue@3.5.13: resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} peerDependencies: @@ -4610,6 +4728,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -5683,6 +5806,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -5809,7 +5934,7 @@ snapshots: chokidar: 4.0.3 emittery: 1.1.0 memoize: 10.1.0 - picomatch: 4.0.2 + picomatch: 4.0.3 slash: 5.1.0 typescript: 5.7.3 @@ -6399,6 +6524,45 @@ snapshots: vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) vue: 3.5.13(typescript@5.7.3) + '@vitest/expect@4.0.14': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.14(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 4.0.14 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + + '@vitest/pretty-format@4.0.14': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.14': + dependencies: + '@vitest/utils': 4.0.14 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.14': {} + + '@vitest/utils@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + tinyrainbow: 3.0.3 + '@vue/compiler-core@3.5.13': dependencies: '@babel/parser': 7.26.9 @@ -6516,9 +6680,7 @@ snapshots: acorn-walk@8.3.4: dependencies: - acorn: 8.14.0 - - acorn@8.14.0: {} + acorn: 8.15.0 acorn@8.15.0: {} @@ -6747,6 +6909,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.1: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -7017,7 +7181,7 @@ snapshots: edge-parser@9.0.4: dependencies: - acorn: 8.14.0 + acorn: 8.15.0 astring: 1.9.0 edge-error: 4.0.2 edge-lexer: 6.0.3 @@ -7081,6 +7245,8 @@ snapshots: es-module-lexer@1.6.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -7237,6 +7403,10 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} etag@1.8.1: {} @@ -7266,6 +7436,8 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 + expect-type@1.2.2: {} + extendable-error@0.1.7: {} fast-copy@3.0.2: {} @@ -7821,6 +7993,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@4.0.0: dependencies: semver: 7.7.3 @@ -7989,6 +8165,8 @@ snapshots: object-inspect@1.13.4: {} + obug@2.1.1: {} + on-exit-leak-free@2.1.2: {} on-finished@2.4.1: @@ -8018,6 +8196,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + optique@0.1.0: {} + outdent@0.5.0: {} p-event@6.0.1: @@ -8105,6 +8285,8 @@ snapshots: path-type@6.0.0: {} + pathe@2.0.3: {} + pathval@2.0.1: {} peek-readable@7.0.0: {} @@ -8626,6 +8808,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} sinon@20.0.0: @@ -8701,6 +8885,8 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + stacktracey@2.1.8: dependencies: as-table: 1.0.55 @@ -8708,6 +8894,8 @@ snapshots: statuses@2.0.1: {} + std-env@3.10.0: {} + streamx@2.23.0: dependencies: events-universal: 1.0.1 @@ -8870,6 +9058,8 @@ snapshots: timekeeper@2.3.1: {} + tinybench@2.9.0: {} + tinyexec@0.3.2: {} tinyexec@1.0.1: {} @@ -8879,6 +9069,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyrainbow@3.0.3: {} + tmp-cache@1.1.0: {} to-regex-range@5.0.1: @@ -8915,7 +9107,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 22.18.13 - acorn: 8.14.0 + acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 @@ -9119,6 +9311,43 @@ snapshots: - universal-cookie - yaml + vitest@4.0.14(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.14 + '@vitest/mocker': 4.0.14(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.14 + '@vitest/runner': 4.0.14 + '@vitest/snapshot': 4.0.14 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.18.13 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vue@3.5.13(typescript@5.7.3): dependencies: '@vue/compiler-dom': 3.5.13 @@ -9140,6 +9369,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wordwrap@1.0.0: {} From 851567194191c46798eaf2d90d969a2a73cec214 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Wed, 26 Nov 2025 16:35:45 +0100 Subject: [PATCH 06/14] Add basic test setup --- components/conformance/package.json | 5 ++-- .../src/tests/datasets/dataset.spec.ts | 5 ++++ .../src/tests/labels/label.spec.ts | 5 ++++ .../src/tests/nodeinfo/nodeinfo.spec.ts | 5 ++++ components/conformance/vitest.config.ts | 7 +++++ pnpm-lock.yaml | 28 +++++++++++++------ 6 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 components/conformance/src/tests/datasets/dataset.spec.ts create mode 100644 components/conformance/src/tests/labels/label.spec.ts create mode 100644 components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts create mode 100644 components/conformance/vitest.config.ts diff --git a/components/conformance/package.json b/components/conformance/package.json index 82a4dda..3bb39b2 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -4,14 +4,15 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "ts-node src/cli.ts" }, "keywords": [], "author": "", "license": "AGPL-3.0", "packageManager": "pnpm@10.9.0", "dependencies": { - "optique": "^0.1.0", + "@optique/core": "^0.7.0", + "@optique/run": "^0.7.0", "ts-node-maintained": "^10.9.6", "typescript": "~5.7.3", "vitest": "^4.0.14" diff --git a/components/conformance/src/tests/datasets/dataset.spec.ts b/components/conformance/src/tests/datasets/dataset.spec.ts new file mode 100644 index 0000000..dd990a4 --- /dev/null +++ b/components/conformance/src/tests/datasets/dataset.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from 'vitest'; + +test('sample test', () => { + expect(1 + 1).toBe(2); +}); \ No newline at end of file diff --git a/components/conformance/src/tests/labels/label.spec.ts b/components/conformance/src/tests/labels/label.spec.ts new file mode 100644 index 0000000..dd990a4 --- /dev/null +++ b/components/conformance/src/tests/labels/label.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from 'vitest'; + +test('sample test', () => { + expect(1 + 1).toBe(2); +}); \ No newline at end of file diff --git a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts new file mode 100644 index 0000000..dd990a4 --- /dev/null +++ b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from 'vitest'; + +test('sample test', () => { + expect(1 + 1).toBe(2); +}); \ No newline at end of file diff --git a/components/conformance/vitest.config.ts b/components/conformance/vitest.config.ts new file mode 100644 index 0000000..5a41614 --- /dev/null +++ b/components/conformance/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + + } +}); \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a80766..45ab220 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,9 +24,12 @@ importers: components/conformance: dependencies: - optique: - specifier: ^0.1.0 - version: 0.1.0 + '@optique/core': + specifier: ^0.7.0 + version: 0.7.0 + '@optique/run': + specifier: ^0.7.0 + version: 0.7.0 ts-node-maintained: specifier: ^10.9.6 version: 10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3) @@ -1144,6 +1147,14 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@optique/core@0.7.0': + resolution: {integrity: sha512-vrf+7HC/4puTDC8ssZ/1rDWB5dtPymRdM7xSp3MkmXEfu72QyBXPWTcXvRu0UFpCFAyZvJfZP1H0IhTM0ZU1lQ==} + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} + + '@optique/run@0.7.0': + resolution: {integrity: sha512-0dx7W/PQh4zFSUxihRyniYP8skhfLbDUKaBdgi5ZOBx+E//hSRwRxAu0jtjNR8k2reYH56+8UGm7E/ClPe6Q3A==} + engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} + '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} @@ -3551,9 +3562,6 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - optique@0.1.0: - resolution: {integrity: sha512-1tnLA5lrcqr5vOUupcorEzLsU2OwhTnArtb2WmjPA+QO4IZjJYX0ESyZw5Nskq0K3wzlo478mclMFb5z4pNA1g==} - outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -5855,6 +5863,12 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.0 + '@optique/core@0.7.0': {} + + '@optique/run@0.7.0': + dependencies: + '@optique/core': 0.7.0 + '@paralleldrive/cuid2@2.2.2': dependencies: '@noble/hashes': 1.7.1 @@ -8196,8 +8210,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - optique@0.1.0: {} - outdent@0.5.0: {} p-event@6.0.1: From 9ca12c42ec976eb2c6ce164f5011688ae269239d Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Thu, 27 Nov 2025 11:03:10 +0100 Subject: [PATCH 07/14] - Add dependencies, configure typescript - Add basic nodeinfo test --- components/conformance/package.json | 1 + components/conformance/src/cli.ts | 127 ++++++++++++++++++ .../src/tests/nodeinfo/nodeinfo.spec.ts | 49 ++++++- components/conformance/src/utils.ts | 68 ++++++++++ components/conformance/tsconfig.json | 25 ++++ pnpm-lock.yaml | 67 +++++++++ 6 files changed, 334 insertions(+), 3 deletions(-) create mode 100644 components/conformance/src/cli.ts create mode 100644 components/conformance/src/utils.ts create mode 100644 components/conformance/tsconfig.json diff --git a/components/conformance/package.json b/components/conformance/package.json index 3bb39b2..890ca76 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -13,6 +13,7 @@ "dependencies": { "@optique/core": "^0.7.0", "@optique/run": "^0.7.0", + "jsonld": "^9.0.0", "ts-node-maintained": "^10.9.6", "typescript": "~5.7.3", "vitest": "^4.0.14" diff --git a/components/conformance/src/cli.ts b/components/conformance/src/cli.ts new file mode 100644 index 0000000..7c83306 --- /dev/null +++ b/components/conformance/src/cli.ts @@ -0,0 +1,127 @@ +#!/usr/bin/env node + +import { merge, object, or } from "@optique/core/constructs"; +import { optional, withDefault } from "@optique/core/modifiers"; +import { option } from "@optique/core/primitives"; +import { choice, string, url } from "@optique/core/valueparser"; +import type { ValueParser, ValueParserResult } from "@optique/core/valueparser"; +import { message, values } from "@optique/core/message"; +import { run } from "@optique/run"; +import { startVitest } from "vitest/node"; + +const AVAILABLE_SUITES = ["labels", "datasets", "nodeinfo"]; + +// Custom value parser for comma-separated suite list +function commaSeparatedSuites(): ValueParser { + return { + metavar: "SUITE[,SUITE...]", + parse(input: string): ValueParserResult { + const suites = input.split(",").map(s => s.trim()).filter(s => s.length > 0); + const invalidSuites = suites.filter(s => !AVAILABLE_SUITES.includes(s)); + + if (invalidSuites.length > 0) { + return { + success: false, + error: message`Invalid suite(s): ${values(invalidSuites)}. Available: ${values(AVAILABLE_SUITES)}`, + }; + } + + return { success: true, value: suites }; + }, + format(suites: string[]): string { + return suites.join(","); + }, + }; +} + +// Base options shared by all configurations +const baseOptions = object({ + url: option("--url", url()), + suites: optional(option("--suites", commaSeparatedSuites())), + verbose: withDefault(option("--verbose"), false), +}); + +// File reporter configuration: requires both --reporter and --output-file +const fileReporterOptions = merge( + baseOptions, + object({ + reporter: option("--reporter", choice(["junit", "html", "json"])), + outputFile: option("--output-file", string()), + }), +); + +// Console reporter configuration: --reporter optional, --output-file not present +const consoleReporterOptions = merge( + baseOptions, + object({ + reporter: withDefault( + optional(option("--reporter", choice(["console"]))), + "console" as const, + ), + noColor: withDefault(option("--no-color"), false), + }), +); + +// Try file reporter first (more specific), then console (more lenient) +const parser = or(fileReporterOptions, consoleReporterOptions); + +async function main() { + const pkg = require("../package.json"); + + // Run the parser with optique + const options = run(parser, { + programName: "fires-conformance", + help: "option", + version: pkg.version, + }); + + // Convert suites to test directories if provided + const testDirs = options.suites?.map((suite) => `src/tests/${suite}`); + + // Transform options for Vitest + const vitestConfig: any = { + run: true, + mode: "test", + env: { + FIRES_SERVER_URL: options.url, + }, + }; + + // Configure reporter based on which parser matched + if ("outputFile" in options) { + // File-based reporter (junit/html/json) + vitestConfig.reporters = [options.reporter]; + vitestConfig.outputFile = options.outputFile; + } else { + // Console reporter - check for color option + if ("noColor" in options && options.noColor) { + vitestConfig.color = false; + } + } + + // Configure logging level + if (options.verbose) { + vitestConfig.logLevel = "info"; + } + + // Run Vitest programmatically + const vitest = await startVitest("test", testDirs || [], vitestConfig); + + if (!vitest) { + console.error("Failed to start Vitest"); + process.exit(1); + } + + // Exit with appropriate code based on test results + const hasFailures = + vitest.state.getUnhandledErrors().length > 0 || + vitest.state.getCountOfFailedTests() > 0; + + await vitest.close(); + process.exit(hasFailures ? 1 : 0); +} + +main().catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); diff --git a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts index dd990a4..f3b0321 100644 --- a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts +++ b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts @@ -1,5 +1,48 @@ -import { test, expect } from 'vitest'; +import { test, expect, describe } from 'vitest'; +import { getServerUrl, getNodeInfo, getLabelsEndpoint, getDatasetsEndpoint } from '../../utils'; -test('sample test', () => { - expect(1 + 1).toBe(2); +describe('NodeInfo', () => { + test('GET /.well-known/nodeinfo returns JSON', async () => { + const response = await fetch(`${getServerUrl()}/.well-known/nodeinfo`); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/json'); + }); + + test('nodeinfo 2.1 endpoint has version 2.1', async () => { + const nodeinfo = await getNodeInfo(); + + expect(nodeinfo.version).toBe('2.1'); + }); + + test('protocols array contains "fires"', async () => { + const nodeinfo = await getNodeInfo(); + + expect(Array.isArray(nodeinfo.protocols)).toBe(true); + expect(nodeinfo.protocols).toContain('fires'); + }); + + test('metadata.fires is an object', async () => { + const nodeinfo = await getNodeInfo(); + + expect(typeof nodeinfo.metadata.fires).toBe('object'); + }); + + test('metadata.fires.labels is a well-formed IRI (optional)', async () => { + const labels = await getLabelsEndpoint(); + + if (labels !== undefined) { + expect(typeof labels).toBe('string'); + expect(() => new URL(labels)).not.toThrow(); + } + }); + + test('metadata.fires.datasets is a well-formed IRI (optional)', async () => { + const datasets = await getDatasetsEndpoint(); + + if (datasets !== undefined) { + expect(typeof datasets).toBe('string'); + expect(() => new URL(datasets)).not.toThrow(); + } + }); }); \ No newline at end of file diff --git a/components/conformance/src/utils.ts b/components/conformance/src/utils.ts new file mode 100644 index 0000000..d50bc0f --- /dev/null +++ b/components/conformance/src/utils.ts @@ -0,0 +1,68 @@ +const SERVER_URL = process.env.FIRES_SERVER_URL || 'http://localhost:3333'; + +interface NodeInfoDiscovery { + links: Array<{ + rel: string; + href: string; + }>; +} + +interface NodeInfo { + version: string; + protocols: string[]; + metadata: { + fires?: { + labels?: string; + datasets?: string; + }; + }; +} + +/** + * Fetches the NodeInfo discovery document from /.well-known/nodeinfo + */ +export async function getNodeInfoDiscovery(): Promise { + const response = await fetch(`${SERVER_URL}/.well-known/nodeinfo`); + return await response.json(); +} + +/** + * Fetches the NodeInfo 2.1 document by following the discovery link + */ +export async function getNodeInfo(): Promise { + const discovery = await getNodeInfoDiscovery(); + + const nodeinfo21Link = discovery.links.find( + (link) => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1' + ); + + if (!nodeinfo21Link) { + throw new Error('NodeInfo 2.1 link not found in discovery document'); + } + + const response = await fetch(nodeinfo21Link.href); + return await response.json(); +} + +/** + * Gets the labels endpoint URL from the NodeInfo metadata + */ +export async function getLabelsEndpoint(): Promise { + const nodeinfo = await getNodeInfo(); + return nodeinfo.metadata?.fires?.labels; +} + +/** + * Gets the datasets endpoint URL from the NodeInfo metadata + */ +export async function getDatasetsEndpoint(): Promise { + const nodeinfo = await getNodeInfo(); + return nodeinfo.metadata?.fires?.datasets; +} + +/** + * Gets the server URL used for tests + */ +export function getServerUrl(): string { + return SERVER_URL; +} diff --git a/components/conformance/tsconfig.json b/components/conformance/tsconfig.json new file mode 100644 index 0000000..ebeb25f --- /dev/null +++ b/components/conformance/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "lib": ["ESNext"], + "noUnusedLocals": true, + "noUnusedParameters": true, + "isolatedModules": true, + "removeComments": true, + "esModuleInterop": true, + "strictNullChecks": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strictPropertyInitialization": true, + "experimentalDecorators": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "skipLibCheck": true + }, + "ts-node": { + "swc": true + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45ab220..c5e98cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: '@optique/run': specifier: ^0.7.0 version: 0.7.0 + jsonld: + specifier: ^9.0.0 + version: 9.0.0 ts-node-maintained: specifier: ^10.9.6 version: 10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3) @@ -747,6 +750,10 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@digitalbazaar/http-client@4.2.0': + resolution: {integrity: sha512-OGju/GYp0V72qlZ/Pd4jGEwqBwT/Za/tw+Z3AC7lgMheGqsbhTZrtc5iLz9z59G/Q53QyE2fnjHV8N9wjBpiWA==} + engines: {node: '>=18.0'} + '@docsearch/css@3.9.0': resolution: {integrity: sha512-cQbnVbq0rrBwNAKegIac/t6a8nWoUAn8frnkLFW6YARaRmAQr5/Eoe6Ln2fqkUCZ40KpdrKbpSAmgrkviOxuWA==} @@ -2183,6 +2190,10 @@ packages: caniuse-lite@1.0.30001751: resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + canonicalize@2.1.0: + resolution: {integrity: sha512-F705O3xrsUtgt98j7leetNhTWPe+5S72rlL5O4jA1pKqBVQ/dT1O1D6PFxmSXvc0SUOinWS57DKx0I3CHrXJHQ==} + hasBin: true + case-anything@3.1.2: resolution: {integrity: sha512-wljhAjDDIv/hM2FzgJnYQg90AWmZMNtESCjTeLH680qTzdo0nErlCxOmgzgX4ZsZAtIvqHyD87ES8QyriXB+BQ==} engines: {node: '>=18'} @@ -3217,6 +3228,10 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonld@9.0.0: + resolution: {integrity: sha512-pjMIdkXfC1T2wrX9B9i2uXhGdyCmgec3qgMht+TDj+S0qX3bjWMQUfL7NeqEhuRTi8G5ESzmL9uGlST7nzSEWg==} + engines: {node: '>=18'} + jsonschema@1.5.0: resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} @@ -3263,6 +3278,10 @@ packages: tedious: optional: true + ky@1.14.0: + resolution: {integrity: sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==} + engines: {node: '>=18'} + lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} @@ -3319,6 +3338,10 @@ packages: resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} engines: {node: 20 || >=22} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -3879,6 +3902,10 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + rdf-canonize@5.0.0: + resolution: {integrity: sha512-g8OUrgMXAR9ys/ZuJVfBr05sPPoMA7nHIVs8VEvg9QwM5W4GR2qSFEEHjsyHF1eWlBaf8Ev40WNjQFQ+nJTO3w==} + engines: {node: '>=18'} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -4146,6 +4173,9 @@ packages: set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -4552,6 +4582,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici@6.22.0: + resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==} + engines: {node: '>=18.17'} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -4775,6 +4809,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.8.1: resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} @@ -5511,6 +5548,11 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@digitalbazaar/http-client@4.2.0': + dependencies: + ky: 1.14.0 + undici: 6.22.0 + '@docsearch/css@3.9.0': {} '@docsearch/js@3.9.0(@algolia/client-search@5.20.4)(search-insights@2.17.3)': @@ -6911,6 +6953,8 @@ snapshots: caniuse-lite@1.0.30001751: {} + canonicalize@2.1.0: {} + case-anything@3.1.2: {} ccount@2.0.1: {} @@ -7897,6 +7941,13 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonld@9.0.0: + dependencies: + '@digitalbazaar/http-client': 4.2.0 + canonicalize: 2.1.0 + lru-cache: 6.0.0 + rdf-canonize: 5.0.0 + jsonschema@1.5.0: {} junk@4.0.1: {} @@ -7942,6 +7993,8 @@ snapshots: transitivePeerDependencies: - supports-color + ky@1.14.0: {} + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 @@ -8001,6 +8054,10 @@ snapshots: lru-cache@11.2.2: {} + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + luxon@3.7.2: {} magic-string@0.30.17: @@ -8488,6 +8545,10 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + rdf-canonize@5.0.0: + dependencies: + setimmediate: 1.0.5 + react-is@18.3.1: {} read-package-up@11.0.0: @@ -8773,6 +8834,8 @@ snapshots: set-cookie-parser@2.7.1: {} + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shebang-command@2.0.0: @@ -9178,6 +9241,8 @@ snapshots: undici-types@6.21.0: {} + undici@6.22.0: {} + unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} @@ -9420,6 +9485,8 @@ snapshots: y18n@5.0.8: {} + yallist@4.0.0: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} From c12937d50e014895640f03772d54d201cc32d162 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Mon, 1 Dec 2025 15:08:10 +0100 Subject: [PATCH 08/14] Add BCP Parsing dependency --- components/conformance/package.json | 1 + pnpm-lock.yaml | 30 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/components/conformance/package.json b/components/conformance/package.json index 890ca76..a882c35 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -13,6 +13,7 @@ "dependencies": { "@optique/core": "^0.7.0", "@optique/run": "^0.7.0", + "bcp-47": "^2.1.0", "jsonld": "^9.0.0", "ts-node-maintained": "^10.9.6", "typescript": "~5.7.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5e98cf..911d221 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,9 @@ importers: '@optique/run': specifier: ^0.7.0 version: 0.7.0 + bcp-47: + specifier: ^2.1.0 + version: 2.1.0 jsonld: specifier: ^9.0.0 version: 9.0.0 @@ -2094,6 +2097,9 @@ packages: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} + bcp-47@2.1.0: + resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} + bcp47-language-tags@1.1.0: resolution: {integrity: sha512-JPPjQjl7NBQf38PSZRM9hx2R4nHeLvN84mkrD/orci80MEq2rVN78hbraxQhp33DhIU/8cNE+/ukt9pVPPKNNg==} @@ -3095,6 +3101,12 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} @@ -3103,6 +3115,9 @@ packages: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -6868,6 +6883,12 @@ snapshots: dependencies: safe-buffer: 5.1.2 + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + bcp47-language-tags@1.1.0: {} bentocache@1.5.0(knex@3.1.0(pg@8.16.3)): @@ -7834,6 +7855,13 @@ snapshots: ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-builtin-module@5.0.0: dependencies: builtin-modules: 5.0.0 @@ -7842,6 +7870,8 @@ snapshots: dependencies: hasown: 2.0.2 + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} From bf42c438b5508e1b9b4f6c6a2849d5ec1f217d8c Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Mon, 1 Dec 2025 15:08:38 +0100 Subject: [PATCH 09/14] Add datasets tests & rename labels for consistency --- .../src/tests/datasets/dataset.spec.ts | 252 ++++++++++++++- .../src/tests/labels/label.spec.ts | 5 - .../src/tests/labels/labels.spec.ts | 289 ++++++++++++++++++ 3 files changed, 538 insertions(+), 8 deletions(-) delete mode 100644 components/conformance/src/tests/labels/label.spec.ts create mode 100644 components/conformance/src/tests/labels/labels.spec.ts diff --git a/components/conformance/src/tests/datasets/dataset.spec.ts b/components/conformance/src/tests/datasets/dataset.spec.ts index dd990a4..0f68a52 100644 --- a/components/conformance/src/tests/datasets/dataset.spec.ts +++ b/components/conformance/src/tests/datasets/dataset.spec.ts @@ -1,5 +1,251 @@ -import { test, expect } from 'vitest'; +import { test, expect, describe } from 'vitest'; +import { getDatasetsEndpoint } from '../../utils'; -test('sample test', () => { - expect(1 + 1).toBe(2); +const EXPECTED_CONTEXT = [ + 'https://www.w3.org/ns/activitystreams', + 'https://fires.fedimod.org/context/fires.jsonld', +]; + +/** + * Validates that a dataset has all required fields + */ +function validateDatasetFields(dataset: any): void { + // Required fields per FIRES spec + expect(dataset.type).toBe('Dataset'); + expect(dataset).toHaveProperty('id'); + expect(typeof dataset.id).toBe('string'); + + // name is required + expect(dataset).toHaveProperty('name'); + expect(typeof dataset.name).toBe('string'); + + // url is required + expect(dataset).toHaveProperty('url'); + expect(typeof dataset.url).toBe('string'); + + // published is required + expect(dataset).toHaveProperty('published'); + expect(typeof dataset.published).toBe('string'); + + // endpoints are required + expect(dataset).toHaveProperty('endpoints'); + expect(typeof dataset.endpoints).toBe('object'); + expect(dataset.endpoints).toHaveProperty('changes'); + expect(typeof dataset.endpoints.changes).toBe('string'); + expect(dataset.endpoints).toHaveProperty('snapshot'); + expect(typeof dataset.endpoints.snapshot).toBe('string'); + + // Optional fields - validate if present + if ('summary' in dataset) { + expect(typeof dataset.summary).toBe('string'); + } + + if ('content' in dataset) { + expect(typeof dataset.content).toBe('string'); + } + + if ('updated' in dataset) { + expect(typeof dataset.updated).toBe('string'); + } +} + +describe('Datasets Collection', () => { + test('datasets endpoint returns valid JSON-LD', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/ld+json'); + + const data: any = await response.json(); + + // Validate JSON-LD structure + expect(data).toHaveProperty('@context'); + expect(data['@context']).toEqual(EXPECTED_CONTEXT); + }); + + test('datasets collection has required fields', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // Collection should have these required fields + expect(data.type).toBe('Collection'); + expect(data).toHaveProperty('totalItems'); + expect(typeof data.totalItems).toBe('number'); + expect(data).toHaveProperty('items'); + expect(Array.isArray(data.items)).toBe(true); + }); + + test('datasets collection items have required structure', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // If there are items, validate their structure + if (data.items && data.items.length > 0) { + for (const dataset of data.items) { + validateDatasetFields(dataset); + } + } + }); +}); + +describe('Individual Datasets', () => { + test('individual dataset endpoint returns valid JSON-LD', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find a dataset + const collectionResponse = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no datasets exist + if (!collection.items || collection.items.length === 0) { + console.log('No datasets found, skipping individual dataset test'); + return; + } + + const datasetId = collection.items[0].id; + + // Fetch the individual dataset + const response = await fetch(datasetId, { + headers: { + Accept: 'application/ld+json', + }, + }); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/ld+json'); + + const data: any = await response.json(); + + // Validate JSON-LD structure + expect(data).toHaveProperty('@context'); + expect(data['@context']).toEqual(EXPECTED_CONTEXT); + }); + + test('individual dataset has required fields', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find a dataset + const collectionResponse = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no datasets exist + if (!collection.items || collection.items.length === 0) { + console.log('No datasets found, skipping individual dataset test'); + return; + } + + const datasetId = collection.items[0].id; + + // Fetch the individual dataset + const response = await fetch(datasetId, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // Validate all required fields + validateDatasetFields(data); + }); + + test('dataset endpoints are accessible', async () => { + const datasetsUrl = await getDatasetsEndpoint(); + + if (!datasetsUrl) { + throw new Error('Datasets endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find a dataset + const collectionResponse = await fetch(datasetsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no datasets exist + if (!collection.items || collection.items.length === 0) { + console.log('No datasets found, skipping endpoints test'); + return; + } + + const datasetId = collection.items[0].id; + + // Fetch the individual dataset + const datasetResponse = await fetch(datasetId, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const dataset: any = await datasetResponse.json(); + + // Verify changes endpoint is accessible + const changesResponse = await fetch(dataset.endpoints.changes, { + headers: { + Accept: 'application/ld+json', + }, + }); + expect(changesResponse.status).toBe(200); + + // Verify snapshot endpoint is accessible + const snapshotResponse = await fetch(dataset.endpoints.snapshot, { + headers: { + Accept: 'application/ld+json', + }, + }); + expect(snapshotResponse.status).toBe(200); + }); }); \ No newline at end of file diff --git a/components/conformance/src/tests/labels/label.spec.ts b/components/conformance/src/tests/labels/label.spec.ts deleted file mode 100644 index dd990a4..0000000 --- a/components/conformance/src/tests/labels/label.spec.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { test, expect } from 'vitest'; - -test('sample test', () => { - expect(1 + 1).toBe(2); -}); \ No newline at end of file diff --git a/components/conformance/src/tests/labels/labels.spec.ts b/components/conformance/src/tests/labels/labels.spec.ts new file mode 100644 index 0000000..4e71e46 --- /dev/null +++ b/components/conformance/src/tests/labels/labels.spec.ts @@ -0,0 +1,289 @@ +import { test, expect, describe } from 'vitest'; +import { parse as parseBCP47 } from 'bcp-47'; +import { getLabelsEndpoint } from '../../utils'; + +const EXPECTED_CONTEXT = [ + 'https://www.w3.org/ns/activitystreams', + 'https://fires.fedimod.org/context/fires.jsonld', +]; + +/** + * Validates that a language tag conforms to BCP-47 + */ +function isValidBCP47(tag: string): boolean { + const parsed = parseBCP47(tag); + return parsed !== null && parsed.language !== undefined; +} + +/** + * Validates localized fields (nameMap, summaryMap, contentMap) + * Each key must be a valid BCP-47 language tag + */ +function validateLocalizedField(field: any): void { + if (field === undefined || field === null) { + return; + } + + expect(typeof field).toBe('object'); + expect(Array.isArray(field)).toBe(false); + + for (const [languageTag, value] of Object.entries(field)) { + expect(isValidBCP47(languageTag)).toBe(true); + expect(typeof value).toBe('string'); + } +} + +/** + * Validates that a label has either simple fields (name, summary, content) + * or localized fields (nameMap, summaryMap, contentMap), but follows BCP-47 for localized ones + */ +function validateLabelFields(label: any): void { + // name or nameMap must exist + const hasName = 'name' in label; + const hasNameMap = 'nameMap' in label; + + expect(hasName || hasNameMap).toBe(true); + + if (hasName) { + expect(typeof label.name).toBe('string'); + } + + if (hasNameMap) { + validateLocalizedField(label.nameMap); + } + + // summary or summaryMap must exist + const hasSummary = 'summary' in label; + const hasSummaryMap = 'summaryMap' in label; + + expect(hasSummary || hasSummaryMap).toBe(true); + + if (hasSummary) { + expect(typeof label.summary).toBe('string'); + } + + if (hasSummaryMap) { + validateLocalizedField(label.summaryMap); + } + + // content or contentMap is optional + if ('content' in label) { + expect(typeof label.content).toBe('string'); + } + + if ('contentMap' in label) { + validateLocalizedField(label.contentMap); + } +} + +describe('Labels Collection', () => { + test('labels endpoint returns valid JSON-LD', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/ld+json'); + + const data: any = await response.json(); + + // Validate JSON-LD structure + expect(data).toHaveProperty('@context'); + expect(data['@context']).toEqual(EXPECTED_CONTEXT); + }); + + test('labels collection has required fields', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // Collection should have these required fields + expect(data.type).toBe('Collection'); + expect(data).toHaveProperty('id'); + expect(data).toHaveProperty('totalItems'); + expect(data).toHaveProperty('items'); + expect(Array.isArray(data.items)).toBe(true); + }); + + test('labels collection items have required structure and valid localization', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + const response = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // If there are items, validate their structure + if (data.items && data.items.length > 0) { + for (const label of data.items) { + expect(label.type).toBe('fires:Label'); + expect(label).toHaveProperty('id'); + + // Validate fields can be either simple or localized with BCP-47 tags + validateLabelFields(label); + } + } + }); +}); + +describe('Individual Labels', () => { + test('individual label endpoint returns valid JSON-LD', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find a label + const collectionResponse = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no labels exist + if (!collection.items || collection.items.length === 0) { + console.log('No labels found, skipping individual label test'); + return; + } + + const labelId = collection.items[0].id; + + // Fetch the individual label + const response = await fetch(labelId, { + headers: { + Accept: 'application/ld+json', + }, + }); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/ld+json'); + + const data: any = await response.json(); + + // Validate JSON-LD structure + expect(data).toHaveProperty('@context'); + expect(data['@context']).toEqual(EXPECTED_CONTEXT); + }); + + test('individual label has required fields and valid localization', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find a label + const collectionResponse = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no labels exist + if (!collection.items || collection.items.length === 0) { + console.log('No labels found, skipping individual label test'); + return; + } + + const labelId = collection.items[0].id; + + // Fetch the individual label + const response = await fetch(labelId, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const data: any = await response.json(); + + // Required fields per FIRES spec + expect(data.type).toBe('fires:Label'); + expect(data).toHaveProperty('id'); + + // Validate fields can be either simple or localized with BCP-47 tags + validateLabelFields(data); + }); + + test('localized fields use valid BCP-47 language tags', async () => { + const labelsUrl = await getLabelsEndpoint(); + + if (!labelsUrl) { + throw new Error('Labels endpoint not found in NodeInfo metadata'); + } + + // First get the collection to find labels + const collectionResponse = await fetch(labelsUrl, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const collection: any = await collectionResponse.json(); + + // Skip test if no labels exist + if (!collection.items || collection.items.length === 0) { + console.log('No labels found, skipping localization test'); + return; + } + + // Test all labels in the collection + for (const labelRef of collection.items) { + const response = await fetch(labelRef.id, { + headers: { + Accept: 'application/ld+json', + }, + }); + + const label: any = await response.json(); + + // Check each localized field if it exists + if ('nameMap' in label) { + for (const tag of Object.keys(label.nameMap)) { + expect(isValidBCP47(tag)).toBe(true); + } + } + + if ('summaryMap' in label) { + for (const tag of Object.keys(label.summaryMap)) { + expect(isValidBCP47(tag)).toBe(true); + } + } + + if ('contentMap' in label) { + for (const tag of Object.keys(label.contentMap)) { + expect(isValidBCP47(tag)).toBe(true); + } + } + } + }); +}); \ No newline at end of file From f422509c528fd68b0cd66a20aedc3fb8929bff1d Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Tue, 9 Dec 2025 15:45:10 +0100 Subject: [PATCH 10/14] Address feedback --- .../src/tests/datasets/dataset.spec.ts | 159 +++++++++++------- .../src/tests/labels/labels.spec.ts | 140 ++++++++------- .../src/tests/nodeinfo/nodeinfo.spec.ts | 41 +++-- components/conformance/src/utils.ts | 10 +- 4 files changed, 200 insertions(+), 150 deletions(-) diff --git a/components/conformance/src/tests/datasets/dataset.spec.ts b/components/conformance/src/tests/datasets/dataset.spec.ts index 0f68a52..cef5fe5 100644 --- a/components/conformance/src/tests/datasets/dataset.spec.ts +++ b/components/conformance/src/tests/datasets/dataset.spec.ts @@ -1,9 +1,9 @@ -import { test, expect, describe } from 'vitest'; -import { getDatasetsEndpoint } from '../../utils'; +import { test, expect, describe } from "vitest"; +import { getDatasetsEndpoint, XSD_DATE_REGEX } from "../../utils"; const EXPECTED_CONTEXT = [ - 'https://www.w3.org/ns/activitystreams', - 'https://fires.fedimod.org/context/fires.jsonld', + "https://www.w3.org/ns/activitystreams", + "https://fires.fedimod.org/context/fires.jsonld", ]; /** @@ -11,101 +11,118 @@ const EXPECTED_CONTEXT = [ */ function validateDatasetFields(dataset: any): void { // Required fields per FIRES spec - expect(dataset.type).toBe('Dataset'); - expect(dataset).toHaveProperty('id'); - expect(typeof dataset.id).toBe('string'); + expect(dataset.type).toBe("Dataset"); + expect(dataset).toHaveProperty("id"); + expect(typeof dataset.id).toBe("string"); + expect(URL.canParse(dataset.id)).toBe(true); // name is required - expect(dataset).toHaveProperty('name'); - expect(typeof dataset.name).toBe('string'); + expect(dataset).toHaveProperty("name"); + expect(typeof dataset.name).toBe("string"); - // url is required - expect(dataset).toHaveProperty('url'); - expect(typeof dataset.url).toBe('string'); + // url is optional + if ("url" in dataset) { + expect(typeof dataset.url).toBe("string"); + expect(URL.canParse(dataset.url)).toBe(true); + } // published is required - expect(dataset).toHaveProperty('published'); - expect(typeof dataset.published).toBe('string'); + expect(dataset).toHaveProperty("published"); + expect(typeof dataset.published).toBe("string"); + expect(XSD_DATE_REGEX.test(dataset.published)).toBe(true); // endpoints are required - expect(dataset).toHaveProperty('endpoints'); - expect(typeof dataset.endpoints).toBe('object'); - expect(dataset.endpoints).toHaveProperty('changes'); - expect(typeof dataset.endpoints.changes).toBe('string'); - expect(dataset.endpoints).toHaveProperty('snapshot'); - expect(typeof dataset.endpoints.snapshot).toBe('string'); + expect(dataset).toHaveProperty("endpoints"); + expect(typeof dataset.endpoints).toBe("object"); + expect(dataset.endpoints).toHaveProperty("changes"); + expect(typeof dataset.endpoints.changes).toBe("string"); + expect(URL.canParse(dataset.endpoints.changes)).toBe(true); + expect(dataset.endpoints).toHaveProperty("snapshot"); + expect(typeof dataset.endpoints.snapshot).toBe("string"); + expect(URL.canParse(dataset.endpoints.snapshot)).toBe(true); // Optional fields - validate if present - if ('summary' in dataset) { - expect(typeof dataset.summary).toBe('string'); + if ("summary" in dataset) { + expect(typeof dataset.summary).toBe("string"); } - if ('content' in dataset) { - expect(typeof dataset.content).toBe('string'); + if ("content" in dataset) { + expect(typeof dataset.content).toBe("string"); } - if ('updated' in dataset) { - expect(typeof dataset.updated).toBe('string'); + if ("updated" in dataset) { + expect(typeof dataset.updated).toBe("string"); + expect(XSD_DATE_REGEX.test(dataset.updated)).toBe(true); } } -describe('Datasets Collection', () => { - test('datasets endpoint returns valid JSON-LD', async () => { +describe("Datasets Collection", () => { + test("datasets endpoint returns valid JSON-LD", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } const response = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(response.status).toBe(200); - expect(response.headers.get('content-type')).toContain('application/ld+json'); + + // Can have further specifiers *after*, per discussion with @ThisIsMissEm. + expect( + response.headers.get("content-type")?.startsWith("application/ld+json"), + ).toBe(true); const data: any = await response.json(); // Validate JSON-LD structure - expect(data).toHaveProperty('@context'); - expect(data['@context']).toEqual(EXPECTED_CONTEXT); + expect(data).toHaveProperty("@context"); + expect(data["@context"]).toEqual(EXPECTED_CONTEXT); }); - test('datasets collection has required fields', async () => { + test("datasets collection has required fields", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } const response = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); const data: any = await response.json(); // Collection should have these required fields - expect(data.type).toBe('Collection'); - expect(data).toHaveProperty('totalItems'); - expect(typeof data.totalItems).toBe('number'); - expect(data).toHaveProperty('items'); + // Both Collection and OrderedCollection are technically interchangeable + expect(["Collection", "OrderedCollection"]).toContain(data.type); + expect(data).toHaveProperty("totalItems"); + expect(typeof data.totalItems).toBe("number"); + expect(Number.isInteger(data.totalItems)).toBe(true); + expect(data.totalItems).toBeGreaterThanOrEqual(0); + + // TODO: support `orderedItems` in case of `OrderedCollection`. + // This probably needs a small rethink. + expect(data).toHaveProperty("items"); expect(Array.isArray(data.items)).toBe(true); }); - test('datasets collection items have required structure', async () => { + test("datasets collection items have required structure", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } const response = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -120,18 +137,18 @@ describe('Datasets Collection', () => { }); }); -describe('Individual Datasets', () => { - test('individual dataset endpoint returns valid JSON-LD', async () => { +describe("Individual Datasets", () => { + test("individual dataset endpoint returns valid JSON-LD", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } // First get the collection to find a dataset const collectionResponse = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -139,7 +156,7 @@ describe('Individual Datasets', () => { // Skip test if no datasets exist if (!collection.items || collection.items.length === 0) { - console.log('No datasets found, skipping individual dataset test'); + console.log("No datasets found, skipping individual dataset test"); return; } @@ -148,31 +165,33 @@ describe('Individual Datasets', () => { // Fetch the individual dataset const response = await fetch(datasetId, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(response.status).toBe(200); - expect(response.headers.get('content-type')).toContain('application/ld+json'); + expect(response.headers.get("content-type")).toContain( + "application/ld+json", + ); const data: any = await response.json(); // Validate JSON-LD structure - expect(data).toHaveProperty('@context'); - expect(data['@context']).toEqual(EXPECTED_CONTEXT); + expect(data).toHaveProperty("@context"); + expect(data["@context"]).toEqual(EXPECTED_CONTEXT); }); - test('individual dataset has required fields', async () => { + test("individual dataset has required fields", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } // First get the collection to find a dataset const collectionResponse = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -180,7 +199,7 @@ describe('Individual Datasets', () => { // Skip test if no datasets exist if (!collection.items || collection.items.length === 0) { - console.log('No datasets found, skipping individual dataset test'); + console.log("No datasets found, skipping individual dataset test"); return; } @@ -189,7 +208,7 @@ describe('Individual Datasets', () => { // Fetch the individual dataset const response = await fetch(datasetId, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -199,17 +218,17 @@ describe('Individual Datasets', () => { validateDatasetFields(data); }); - test('dataset endpoints are accessible', async () => { + test("dataset endpoints are accessible", async () => { const datasetsUrl = await getDatasetsEndpoint(); if (!datasetsUrl) { - throw new Error('Datasets endpoint not found in NodeInfo metadata'); + throw new Error("Datasets endpoint not found in NodeInfo metadata"); } // First get the collection to find a dataset const collectionResponse = await fetch(datasetsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -217,7 +236,7 @@ describe('Individual Datasets', () => { // Skip test if no datasets exist if (!collection.items || collection.items.length === 0) { - console.log('No datasets found, skipping endpoints test'); + console.log("No datasets found, skipping endpoints test"); return; } @@ -226,7 +245,7 @@ describe('Individual Datasets', () => { // Fetch the individual dataset const datasetResponse = await fetch(datasetId, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -235,17 +254,27 @@ describe('Individual Datasets', () => { // Verify changes endpoint is accessible const changesResponse = await fetch(dataset.endpoints.changes, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(changesResponse.status).toBe(200); + expect( + changesResponse.headers + .get("content-type") + ?.startsWith("application/ld+json"), + ).toBe(true); // Verify snapshot endpoint is accessible const snapshotResponse = await fetch(dataset.endpoints.snapshot, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(snapshotResponse.status).toBe(200); + expect( + snapshotResponse.headers + .get("content-type") + ?.startsWith("application/ld+json"), + ).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/components/conformance/src/tests/labels/labels.spec.ts b/components/conformance/src/tests/labels/labels.spec.ts index 4e71e46..2395f49 100644 --- a/components/conformance/src/tests/labels/labels.spec.ts +++ b/components/conformance/src/tests/labels/labels.spec.ts @@ -1,10 +1,10 @@ -import { test, expect, describe } from 'vitest'; -import { parse as parseBCP47 } from 'bcp-47'; -import { getLabelsEndpoint } from '../../utils'; +import { test, expect, describe } from "vitest"; +import { parse as parseBCP47 } from "bcp-47"; +import { getLabelsEndpoint } from "../../utils"; const EXPECTED_CONTEXT = [ - 'https://www.w3.org/ns/activitystreams', - 'https://fires.fedimod.org/context/fires.jsonld', + "https://www.w3.org/ns/activitystreams", + "https://fires.fedimod.org/context/fires.jsonld", ]; /** @@ -24,12 +24,12 @@ function validateLocalizedField(field: any): void { return; } - expect(typeof field).toBe('object'); + expect(typeof field).toBe("object"); expect(Array.isArray(field)).toBe(false); for (const [languageTag, value] of Object.entries(field)) { expect(isValidBCP47(languageTag)).toBe(true); - expect(typeof value).toBe('string'); + expect(typeof value).toBe("string"); } } @@ -39,13 +39,13 @@ function validateLocalizedField(field: any): void { */ function validateLabelFields(label: any): void { // name or nameMap must exist - const hasName = 'name' in label; - const hasNameMap = 'nameMap' in label; + const hasName = "name" in label; + const hasNameMap = "nameMap" in label; expect(hasName || hasNameMap).toBe(true); if (hasName) { - expect(typeof label.name).toBe('string'); + expect(typeof label.name).toBe("string"); } if (hasNameMap) { @@ -53,13 +53,13 @@ function validateLabelFields(label: any): void { } // summary or summaryMap must exist - const hasSummary = 'summary' in label; - const hasSummaryMap = 'summaryMap' in label; + const hasSummary = "summary" in label; + const hasSummaryMap = "summaryMap" in label; expect(hasSummary || hasSummaryMap).toBe(true); if (hasSummary) { - expect(typeof label.summary).toBe('string'); + expect(typeof label.summary).toBe("string"); } if (hasSummaryMap) { @@ -67,72 +67,74 @@ function validateLabelFields(label: any): void { } // content or contentMap is optional - if ('content' in label) { - expect(typeof label.content).toBe('string'); + if ("content" in label) { + expect(typeof label.content).toBe("string"); } - if ('contentMap' in label) { + if ("contentMap" in label) { validateLocalizedField(label.contentMap); } } -describe('Labels Collection', () => { - test('labels endpoint returns valid JSON-LD', async () => { +describe("Labels Collection", () => { + test("labels endpoint returns valid JSON-LD", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } const response = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(response.status).toBe(200); - expect(response.headers.get('content-type')).toContain('application/ld+json'); + expect(response.headers.get("content-type")).toContain( + "application/ld+json", + ); const data: any = await response.json(); // Validate JSON-LD structure - expect(data).toHaveProperty('@context'); - expect(data['@context']).toEqual(EXPECTED_CONTEXT); + expect(data).toHaveProperty("@context"); + expect(data["@context"]).toEqual(EXPECTED_CONTEXT); }); - test('labels collection has required fields', async () => { + test("labels collection has required fields", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } const response = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); const data: any = await response.json(); // Collection should have these required fields - expect(data.type).toBe('Collection'); - expect(data).toHaveProperty('id'); - expect(data).toHaveProperty('totalItems'); - expect(data).toHaveProperty('items'); + expect(data.type).toBe("Collection"); + expect(data).toHaveProperty("id"); + expect(data).toHaveProperty("totalItems"); + expect(data).toHaveProperty("items"); expect(Array.isArray(data.items)).toBe(true); }); - test('labels collection items have required structure and valid localization', async () => { + test("labels collection items have required structure and valid localization", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } const response = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -140,29 +142,36 @@ describe('Labels Collection', () => { // If there are items, validate their structure if (data.items && data.items.length > 0) { - for (const label of data.items) { - expect(label.type).toBe('fires:Label'); - expect(label).toHaveProperty('id'); - - // Validate fields can be either simple or localized with BCP-47 tags - validateLabelFields(label); + for (const item of data.items) { + // Items can be either string URIs or objects with at minimum type + id + if (typeof item === "string") { + // String URI - validate it's parseable as a URL + expect(URL.canParse(item)).toBe(true); + } else { + // Object - must have at minimum type and id + expect(item.type).toBe("fires:Label"); + expect(item).toHaveProperty("id"); + + // Validate fields can be either simple or localized with BCP-47 tags + validateLabelFields(item); + } } } }); }); -describe('Individual Labels', () => { - test('individual label endpoint returns valid JSON-LD', async () => { +describe("Individual Labels", () => { + test("individual label endpoint returns valid JSON-LD", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } // First get the collection to find a label const collectionResponse = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -170,7 +179,7 @@ describe('Individual Labels', () => { // Skip test if no labels exist if (!collection.items || collection.items.length === 0) { - console.log('No labels found, skipping individual label test'); + console.log("No labels found, skipping individual label test"); return; } @@ -179,31 +188,33 @@ describe('Individual Labels', () => { // Fetch the individual label const response = await fetch(labelId, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); expect(response.status).toBe(200); - expect(response.headers.get('content-type')).toContain('application/ld+json'); + expect(response.headers.get("content-type")).toContain( + "application/ld+json", + ); const data: any = await response.json(); // Validate JSON-LD structure - expect(data).toHaveProperty('@context'); - expect(data['@context']).toEqual(EXPECTED_CONTEXT); + expect(data).toHaveProperty("@context"); + expect(data["@context"]).toEqual(EXPECTED_CONTEXT); }); - test('individual label has required fields and valid localization', async () => { + test("individual label has required fields and valid localization", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } // First get the collection to find a label const collectionResponse = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -211,7 +222,7 @@ describe('Individual Labels', () => { // Skip test if no labels exist if (!collection.items || collection.items.length === 0) { - console.log('No labels found, skipping individual label test'); + console.log("No labels found, skipping individual label test"); return; } @@ -220,31 +231,32 @@ describe('Individual Labels', () => { // Fetch the individual label const response = await fetch(labelId, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); const data: any = await response.json(); // Required fields per FIRES spec - expect(data.type).toBe('fires:Label'); - expect(data).toHaveProperty('id'); + // TODO: adjust to FEP standard labels when published + expect(data.type).toBe("fires:Label"); + expect(data).toHaveProperty("id"); // Validate fields can be either simple or localized with BCP-47 tags validateLabelFields(data); }); - test('localized fields use valid BCP-47 language tags', async () => { + test("localized fields use valid BCP-47 language tags", async () => { const labelsUrl = await getLabelsEndpoint(); if (!labelsUrl) { - throw new Error('Labels endpoint not found in NodeInfo metadata'); + throw new Error("Labels endpoint not found in NodeInfo metadata"); } // First get the collection to find labels const collectionResponse = await fetch(labelsUrl, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); @@ -252,7 +264,7 @@ describe('Individual Labels', () => { // Skip test if no labels exist if (!collection.items || collection.items.length === 0) { - console.log('No labels found, skipping localization test'); + console.log("No labels found, skipping localization test"); return; } @@ -260,30 +272,30 @@ describe('Individual Labels', () => { for (const labelRef of collection.items) { const response = await fetch(labelRef.id, { headers: { - Accept: 'application/ld+json', + Accept: "application/ld+json", }, }); const label: any = await response.json(); // Check each localized field if it exists - if ('nameMap' in label) { + if ("nameMap" in label) { for (const tag of Object.keys(label.nameMap)) { expect(isValidBCP47(tag)).toBe(true); } } - if ('summaryMap' in label) { + if ("summaryMap" in label) { for (const tag of Object.keys(label.summaryMap)) { expect(isValidBCP47(tag)).toBe(true); } } - if ('contentMap' in label) { + if ("contentMap" in label) { for (const tag of Object.keys(label.contentMap)) { expect(isValidBCP47(tag)).toBe(true); } } } }); -}); \ No newline at end of file +}); diff --git a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts index f3b0321..a46c407 100644 --- a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts +++ b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts @@ -1,48 +1,53 @@ -import { test, expect, describe } from 'vitest'; -import { getServerUrl, getNodeInfo, getLabelsEndpoint, getDatasetsEndpoint } from '../../utils'; - -describe('NodeInfo', () => { - test('GET /.well-known/nodeinfo returns JSON', async () => { +import { test, expect, describe } from "vitest"; +import { + getServerUrl, + getNodeInfo, + getLabelsEndpoint, + getDatasetsEndpoint, +} from "../../utils"; + +describe("NodeInfo", () => { + test("GET /.well-known/nodeinfo returns JSON", async () => { const response = await fetch(`${getServerUrl()}/.well-known/nodeinfo`); expect(response.status).toBe(200); - expect(response.headers.get('content-type')).toContain('application/json'); + expect(response.headers.get("content-type")).toContain("application/json"); }); - test('nodeinfo 2.1 endpoint has version 2.1', async () => { + test("nodeinfo 2.1 endpoint has version 2.1", async () => { const nodeinfo = await getNodeInfo(); - expect(nodeinfo.version).toBe('2.1'); + expect(nodeinfo.version).toBe("2.1"); }); test('protocols array contains "fires"', async () => { const nodeinfo = await getNodeInfo(); expect(Array.isArray(nodeinfo.protocols)).toBe(true); - expect(nodeinfo.protocols).toContain('fires'); + expect(nodeinfo.protocols).toContain("fires"); }); - test('metadata.fires is an object', async () => { + test("metadata.fires is an object", async () => { const nodeinfo = await getNodeInfo(); - expect(typeof nodeinfo.metadata.fires).toBe('object'); + expect(typeof nodeinfo.metadata.fires).toBe("object"); }); - test('metadata.fires.labels is a well-formed IRI (optional)', async () => { + test("metadata.fires.labels is a well-formed IRI (optional)", async () => { const labels = await getLabelsEndpoint(); if (labels !== undefined) { - expect(typeof labels).toBe('string'); - expect(() => new URL(labels)).not.toThrow(); + expect(typeof labels).toBe("string"); + expect(URL.canParse(labels)).toBe(true); } }); - test('metadata.fires.datasets is a well-formed IRI (optional)', async () => { + test("metadata.fires.datasets is a well-formed IRI (optional)", async () => { const datasets = await getDatasetsEndpoint(); if (datasets !== undefined) { - expect(typeof datasets).toBe('string'); - expect(() => new URL(datasets)).not.toThrow(); + expect(typeof datasets).toBe("string"); + expect(URL.canParse(datasets)).toBe(true); } }); -}); \ No newline at end of file +}); diff --git a/components/conformance/src/utils.ts b/components/conformance/src/utils.ts index d50bc0f..45290fd 100644 --- a/components/conformance/src/utils.ts +++ b/components/conformance/src/utils.ts @@ -1,4 +1,8 @@ -const SERVER_URL = process.env.FIRES_SERVER_URL || 'http://localhost:3333'; +const SERVER_URL = process.env.FIRES_SERVER_URL || "http://localhost:3333"; + +// XSD date format regex: yyyy-MM-dd'T'HH:mm:ss'Z' +// Matches the server's XSDDateFormat from app/utils/jsonld.ts +export const XSD_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/; interface NodeInfoDiscovery { links: Array<{ @@ -33,11 +37,11 @@ export async function getNodeInfo(): Promise { const discovery = await getNodeInfoDiscovery(); const nodeinfo21Link = discovery.links.find( - (link) => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1' + (link) => link.rel === "http://nodeinfo.diaspora.software/ns/schema/2.1", ); if (!nodeinfo21Link) { - throw new Error('NodeInfo 2.1 link not found in discovery document'); + throw new Error("NodeInfo 2.1 link not found in discovery document"); } const response = await fetch(nodeinfo21Link.href); From 6e9699fba99e171d89a1999b0ee91b00964edbe4 Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Thu, 11 Dec 2025 12:04:41 +0100 Subject: [PATCH 11/14] Make CLI function by way of new Optique `conditional` combinator. --- components/conformance/package.json | 4 +- components/conformance/src/cli.ts | 75 ++-- components/conformance/src/utils.ts | 8 +- pnpm-lock.yaml | 661 +++++++++++++++++++--------- 4 files changed, 485 insertions(+), 263 deletions(-) diff --git a/components/conformance/package.json b/components/conformance/package.json index a882c35..deb047e 100644 --- a/components/conformance/package.json +++ b/components/conformance/package.json @@ -11,8 +11,8 @@ "license": "AGPL-3.0", "packageManager": "pnpm@10.9.0", "dependencies": { - "@optique/core": "^0.7.0", - "@optique/run": "^0.7.0", + "@optique/core": "^0.8.0", + "@optique/run": "^0.8.0", "bcp-47": "^2.1.0", "jsonld": "^9.0.0", "ts-node-maintained": "^10.9.6", diff --git a/components/conformance/src/cli.ts b/components/conformance/src/cli.ts index 7c83306..49c7554 100644 --- a/components/conformance/src/cli.ts +++ b/components/conformance/src/cli.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { merge, object, or } from "@optique/core/constructs"; +import { merge, object, conditional } from "@optique/core/constructs"; import { optional, withDefault } from "@optique/core/modifiers"; import { option } from "@optique/core/primitives"; import { choice, string, url } from "@optique/core/valueparser"; @@ -16,8 +16,11 @@ function commaSeparatedSuites(): ValueParser { return { metavar: "SUITE[,SUITE...]", parse(input: string): ValueParserResult { - const suites = input.split(",").map(s => s.trim()).filter(s => s.length > 0); - const invalidSuites = suites.filter(s => !AVAILABLE_SUITES.includes(s)); + const suites = input + .split(",") + .map((s) => s.trim()) + .filter((s) => s.length > 0); + const invalidSuites = suites.filter((s) => !AVAILABLE_SUITES.includes(s)); if (invalidSuites.length > 0) { return { @@ -34,37 +37,33 @@ function commaSeparatedSuites(): ValueParser { }; } +const consoleParser = object({ + noColor: withDefault(option("--no-color"), false), +}); + // Base options shared by all configurations -const baseOptions = object({ +const parser = object({ url: option("--url", url()), suites: optional(option("--suites", commaSeparatedSuites())), verbose: withDefault(option("--verbose"), false), + reporter: conditional( + option("--reporter", choice(["console", "junit", "html", "json"])), + { + console: consoleParser, + junit: object({ + outputFile: option("--output-file", string()), + }), + html: object({ + outputFile: option("--output-file", string()), + }), + json: object({ + outputFile: option("--output-file", string()), + }), + }, + consoleParser, + ), }); -// File reporter configuration: requires both --reporter and --output-file -const fileReporterOptions = merge( - baseOptions, - object({ - reporter: option("--reporter", choice(["junit", "html", "json"])), - outputFile: option("--output-file", string()), - }), -); - -// Console reporter configuration: --reporter optional, --output-file not present -const consoleReporterOptions = merge( - baseOptions, - object({ - reporter: withDefault( - optional(option("--reporter", choice(["console"]))), - "console" as const, - ), - noColor: withDefault(option("--no-color"), false), - }), -); - -// Try file reporter first (more specific), then console (more lenient) -const parser = or(fileReporterOptions, consoleReporterOptions); - async function main() { const pkg = require("../package.json"); @@ -83,20 +82,20 @@ async function main() { run: true, mode: "test", env: { - FIRES_SERVER_URL: options.url, + FIRES_SERVER_URL: options.url.href, }, }; - // Configure reporter based on which parser matched - if ("outputFile" in options) { + const [reporter, reporterConfig] = options.reporter; + + if (reporter === "console" || typeof reporter === "undefined") { + vitestConfig.color = !reporterConfig.noColor; + } + + if (reporter === "html" || reporter === "json" || reporter === "junit") { // File-based reporter (junit/html/json) - vitestConfig.reporters = [options.reporter]; - vitestConfig.outputFile = options.outputFile; - } else { - // Console reporter - check for color option - if ("noColor" in options && options.noColor) { - vitestConfig.color = false; - } + vitestConfig.reporters = [reporter]; + vitestConfig.outputFile = reporterConfig.outputFile; } // Configure logging level diff --git a/components/conformance/src/utils.ts b/components/conformance/src/utils.ts index 45290fd..68e146e 100644 --- a/components/conformance/src/utils.ts +++ b/components/conformance/src/utils.ts @@ -1,4 +1,4 @@ -const SERVER_URL = process.env.FIRES_SERVER_URL || "http://localhost:3333"; +const SERVER_URL = process.env.FIRES_SERVER_URL || "http://localhost:4444"; // XSD date format regex: yyyy-MM-dd'T'HH:mm:ss'Z' // Matches the server's XSDDateFormat from app/utils/jsonld.ts @@ -26,8 +26,8 @@ interface NodeInfo { * Fetches the NodeInfo discovery document from /.well-known/nodeinfo */ export async function getNodeInfoDiscovery(): Promise { - const response = await fetch(`${SERVER_URL}/.well-known/nodeinfo`); - return await response.json(); + const response = await fetch(`${getServerUrl()}.well-known/nodeinfo`); + return (await response.json()) as NodeInfoDiscovery; } /** @@ -45,7 +45,7 @@ export async function getNodeInfo(): Promise { } const response = await fetch(nodeinfo21Link.href); - return await response.json(); + return (await response.json()) as NodeInfo; } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 911d221..8916847 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,10 +14,10 @@ importers: version: 0.5.2 '@changesets/cli': specifier: ^2.29.8 - version: 2.29.8(@types/node@22.18.13) + version: 2.29.8(@types/node@22.19.2) prettier: specifier: ^3.5.3 - version: 3.6.2 + version: 3.7.4 rimraf: specifier: ^6.1.2 version: 6.1.2 @@ -25,11 +25,11 @@ importers: components/conformance: dependencies: '@optique/core': - specifier: ^0.7.0 - version: 0.7.0 + specifier: ^0.8.0 + version: 0.8.0 '@optique/run': - specifier: ^0.7.0 - version: 0.7.0 + specifier: ^0.8.0 + version: 0.8.0 bcp-47: specifier: ^2.1.0 version: 2.1.0 @@ -38,19 +38,19 @@ importers: version: 9.0.0 ts-node-maintained: specifier: ^10.9.6 - version: 10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3) + version: 10.9.6(@swc/core@1.10.18)(@types/node@22.19.2)(typescript@5.7.3) typescript: specifier: ~5.7.3 version: 5.7.3 vitest: specifier: ^4.0.14 - version: 4.0.14(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + version: 4.0.15(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) components/fires-server: dependencies: '@adonisjs/auth': specifier: ^9.4.0 - version: 9.5.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(@japa/plugin-adonisjs@4.0.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(@japa/runner@4.4.0)) + version: 9.5.1(87f402ab40766c56e35b866a0211dc6f) '@adonisjs/bouncer': specifier: ^3.1.6 version: 3.1.6(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(edge.js@6.3.0) @@ -77,16 +77,16 @@ importers: version: 21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3) '@adonisjs/session': specifier: ^7.5.1 - version: 7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) + version: 7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) '@adonisjs/shield': specifier: ^8.2.0 - version: 8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) + version: 8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) '@adonisjs/static': specifier: ^1.1.1 version: 1.1.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0)) '@adonisjs/vite': specifier: ^4.0.0 - version: 4.0.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/shield@8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(edge.js@6.3.0)(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) + version: 4.0.0(be3e457b6044c7bb333bb5ee47a41fa1) '@picocss/pico': specifier: ^2.0.6 version: 2.1.1 @@ -147,7 +147,7 @@ importers: version: 7.8.2(typescript@5.7.3) '@adonisjs/eslint-config': specifier: ^2.1.0 - version: 2.1.2(eslint@9.39.0)(prettier@3.6.2)(typescript@5.7.3) + version: 2.1.2(eslint@9.39.1)(prettier@3.7.4)(typescript@5.7.3) '@adonisjs/prettier-config': specifier: ^1.4.5 version: 1.4.5 @@ -177,7 +177,7 @@ importers: version: 14.1.2 '@types/node': specifier: ^22.13.2 - version: 22.18.13 + version: 22.19.2 '@types/proxy-addr': specifier: ^2.0.3 version: 2.0.3 @@ -192,7 +192,7 @@ importers: version: 10.1.3 eslint: specifier: ^9.20.1 - version: 9.39.0 + version: 9.39.1 hot-hook: specifier: ^0.4.0 version: 0.4.0 @@ -201,10 +201,10 @@ importers: version: 6.6.0 pino-pretty: specifier: ^13.0.0 - version: 13.1.2 + version: 13.1.3 prettier: specifier: ^3.5.0 - version: 3.6.2 + version: 3.7.4 rimraf: specifier: ^6.0.1 version: 6.1.2 @@ -219,31 +219,31 @@ importers: version: 2.3.1 ts-node-maintained: specifier: ^10.9.5 - version: 10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3) + version: 10.9.6(@swc/core@1.10.18)(@types/node@22.19.2)(typescript@5.7.3) typescript: specifier: ~5.7 version: 5.7.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + version: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) docs: devDependencies: prettier: specifier: ^3.5.3 - version: 3.6.2 + version: 3.7.4 rimraf: specifier: ^6.0.1 version: 6.1.2 rollup: specifier: ^4.34.9 - version: 4.52.5 + version: 4.53.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + version: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) vitepress: specifier: 2.0.0-alpha.3 - version: 2.0.0-alpha.3(@algolia/client-search@5.20.4)(@types/node@22.18.13)(change-case@5.4.4)(postcss@8.5.6)(sass-embedded@1.93.3)(sass@1.93.3)(search-insights@2.17.3)(typescript@5.7.3)(yaml@2.8.1) + version: 2.0.0-alpha.3(@algolia/client-search@5.20.4)(@types/node@22.19.2)(change-case@5.4.4)(postcss@8.5.6)(sass-embedded@1.93.3)(sass@1.93.3)(search-insights@2.17.3)(typescript@5.7.3)(yaml@2.8.1) packages: @@ -481,11 +481,12 @@ packages: resolution: {integrity: sha512-NnczRJusl0082GOjEFYwObW/yBGJtpfJFnkFCQdQ6eykgBHVNYaY6qstfob+l1bedetRj0hrDY6YfsWMkA0MCg==} engines: {node: '>=18.16.0'} - '@adonisjs/session@7.5.1': - resolution: {integrity: sha512-b1E0W/1nnJfAq3Gv8yPywgsxJ7uzzOBJxxulonXI4t1eSdvJzZGNrFScfVLOcjTwlxwrEFA847tULIQxgR4Spw==} + '@adonisjs/session@7.6.0': + resolution: {integrity: sha512-EuOcwWPUoFB9OQmENwv4GpAaczyAT8kDS+HAFse/Yz2VTjY1KxzmTfcmLIGkAxN9lU3h5TwoHKgVXLF373ORUQ==} engines: {node: '>=18.16.0'} peerDependencies: '@adonisjs/core': ^6.6.0 + '@adonisjs/lucid': ^21.0.0 '@adonisjs/redis': ^8.0.1 || ^9.0.0 '@aws-sdk/client-dynamodb': ^3.658.0 '@aws-sdk/util-dynamodb': ^3.658.0 @@ -493,6 +494,8 @@ packages: '@japa/browser-client': ^2.0.3 edge.js: ^6.0.2 peerDependenciesMeta: + '@adonisjs/lucid': + optional: true '@adonisjs/redis': optional: true '@aws-sdk/client-dynamodb': @@ -966,8 +969,8 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.0': - resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==} + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -1145,6 +1148,10 @@ packages: resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1157,17 +1164,20 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@optique/core@0.7.0': - resolution: {integrity: sha512-vrf+7HC/4puTDC8ssZ/1rDWB5dtPymRdM7xSp3MkmXEfu72QyBXPWTcXvRu0UFpCFAyZvJfZP1H0IhTM0ZU1lQ==} + '@optique/core@0.8.0': + resolution: {integrity: sha512-LB4DD9ir7yB0EeUmV8TkC7/SkxLzFSBcNaETKr5QPG2xNPQ9FLprO/DNfa2spuHO1bAG2i/5q2o0nk4o3TDVwA==} engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} - '@optique/run@0.7.0': - resolution: {integrity: sha512-0dx7W/PQh4zFSUxihRyniYP8skhfLbDUKaBdgi5ZOBx+E//hSRwRxAu0jtjNR8k2reYH56+8UGm7E/ClPe6Q3A==} + '@optique/run@0.8.0': + resolution: {integrity: sha512-sg5qKamPiSvUR+7Ih1l3zpFchyQZzncIy0G6eJ+iV6j9jYViphMULQfgCpQOHuU6NdYjNSWlagg5UqVa6awfdw==} engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@paralleldrive/cuid2@2.3.1': + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -1292,10 +1302,6 @@ packages: resolution: {integrity: sha512-WLneXKQYNClhaMXccO111VQmZahSrcSRDaHRbV6KL5R4pTvK87fMn/MXLUcvOjk0X5dTHDPKF61tM7j826wrjQ==} engines: {node: '>=20.6.0'} - '@poppinss/exception@1.2.1': - resolution: {integrity: sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A==} - engines: {node: '>=18'} - '@poppinss/exception@1.2.2': resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} @@ -1353,10 +1359,6 @@ packages: resolution: {integrity: sha512-ypVszZxhwiehhklM5so2BI+nClQJwp7mBUSJh/R1GepeUH1vvD5GtxMz8Lp9dO9oAbKyDmq1jc4g/4E0dv8r2g==} engines: {node: '>=18.16.0'} - '@poppinss/utils@6.9.4': - resolution: {integrity: sha512-KJe9/ebFBqb4fFBdadgN4YgT4bHAKdWhLAFzjaeDqx5vOCtD3C+byN5DrORVNbwAjt+rb8beP8pXaWZWx+WmTA==} - engines: {node: '>=18.16.0'} - '@poppinss/utils@7.0.0-next.3': resolution: {integrity: sha512-Z+3kolI/gdjTQRJWRiZTEk6r6QOeGLesfmdc8ISSeHlyUg0mTnVdi08/rwOcRJD6dLdwBGTDUf7lK2K7GpT4ww==} @@ -1368,111 +1370,221 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.52.5': resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.52.5': resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.52.5': resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.52.5': resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.52.5': resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.52.5': resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.52.5': resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.52.5': resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.52.5': resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.52.5': resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.52.5': resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.52.5': resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.52.5': resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.52.5': resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.52.5': resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + '@rollup/rollup-openharmony-arm64@4.52.5': resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.52.5': resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.52.5': resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.52.5': resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.52.5': resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -1701,8 +1813,8 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@22.18.13': - resolution: {integrity: sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==} + '@types/node@22.19.2': + resolution: {integrity: sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1822,11 +1934,11 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/expect@4.0.14': - resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} - '@vitest/mocker@4.0.14': - resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -1836,20 +1948,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.14': - resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} - '@vitest/runner@4.0.14': - resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} - '@vitest/snapshot@4.0.14': - resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} - '@vitest/spy@4.0.14': - resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} - '@vitest/utils@4.0.14': - resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} @@ -2351,6 +2463,10 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} @@ -2689,8 +2805,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.0: - resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==} + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2760,8 +2876,8 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-copy@3.0.2: - resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-copy@4.0.1: + resolution: {integrity: sha512-+uUOQlhsaswsizHFmEFAQhB3lSiQ+lisxl50N6ZP0wywlZeWsIESxSi9ftPEps8UGfiBzyYP7x27zA674WUvXw==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2865,8 +2981,8 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} formidable@3.5.4: @@ -3361,9 +3477,6 @@ packages: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -3783,8 +3896,11 @@ packages: pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} - pino-pretty@13.1.2: - resolution: {integrity: sha512-3cN0tCakkT4f3zo9RXDIhy6GTvtYD6bK4CRBLN9j3E/ePqN1tugAXD5rGVfoChW6s0hiek+eyYlLNqc/BG7vBQ==} + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true pino-std-serializers@7.0.0: @@ -3841,8 +3957,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -4026,6 +4142,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4188,6 +4309,9 @@ packages: set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -4387,10 +4511,9 @@ packages: resolution: {integrity: sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==} engines: {node: '>=18'} - superagent@10.2.1: - resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} + superagent@10.2.3: + resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} engines: {node: '>=14.18.0'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net superjson@2.2.2: resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} @@ -4477,8 +4600,9 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.1: - resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -4732,18 +4856,18 @@ packages: postcss: optional: true - vitest@4.0.14: - resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.14 - '@vitest/browser-preview': 4.0.14 - '@vitest/browser-webdriverio': 4.0.14 - '@vitest/ui': 4.0.14 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4919,7 +5043,7 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - '@adonisjs/auth@9.5.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(@japa/plugin-adonisjs@4.0.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(@japa/runner@4.4.0))': + '@adonisjs/auth@9.5.1(87f402ab40766c56e35b866a0211dc6f)': dependencies: '@adonisjs/core': 6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0) '@adonisjs/presets': 2.6.4(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0)) @@ -4927,7 +5051,7 @@ snapshots: basic-auth: 2.0.1 optionalDependencies: '@adonisjs/lucid': 21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3) - '@adonisjs/session': 7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) + '@adonisjs/session': 7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) '@japa/api-client': 3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0) '@japa/plugin-adonisjs': 4.0.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(@japa/runner@4.4.0) transitivePeerDependencies: @@ -5036,25 +5160,25 @@ snapshots: dotenv: 16.5.0 split-lines: 3.0.0 - '@adonisjs/eslint-config@2.1.2(eslint@9.39.0)(prettier@3.6.2)(typescript@5.7.3)': + '@adonisjs/eslint-config@2.1.2(eslint@9.39.1)(prettier@3.7.4)(typescript@5.7.3)': dependencies: - '@adonisjs/eslint-plugin': 2.0.1(eslint@9.39.0)(typescript@5.7.3) - '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.0) - eslint: 9.39.0 - eslint-config-prettier: 10.1.8(eslint@9.39.0) - eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.0))(eslint@9.39.0)(prettier@3.6.2) - eslint-plugin-unicorn: 60.0.0(eslint@9.39.0) - prettier: 3.6.2 - typescript-eslint: 8.46.2(eslint@9.39.0)(typescript@5.7.3) + '@adonisjs/eslint-plugin': 2.0.1(eslint@9.39.1)(typescript@5.7.3) + '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.1) + eslint: 9.39.1 + eslint-config-prettier: 10.1.8(eslint@9.39.1) + eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.7.4) + eslint-plugin-unicorn: 60.0.0(eslint@9.39.1) + prettier: 3.7.4 + typescript-eslint: 8.46.2(eslint@9.39.1)(typescript@5.7.3) transitivePeerDependencies: - '@types/eslint' - supports-color - typescript - '@adonisjs/eslint-plugin@2.0.1(eslint@9.39.0)(typescript@5.7.3)': + '@adonisjs/eslint-plugin@2.0.1(eslint@9.39.1)(typescript@5.7.3)': dependencies: - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0)(typescript@5.7.3) - eslint: 9.39.0 + '@typescript-eslint/utils': 8.46.2(eslint@9.39.1)(typescript@5.7.3) + eslint: 9.39.1 transitivePeerDependencies: - supports-color - typescript @@ -5188,19 +5312,20 @@ snapshots: '@poppinss/colors': 4.1.5 string-width: 7.2.0 - '@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0)': + '@adonisjs/session@7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0)': dependencies: '@adonisjs/core': 6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0) '@poppinss/macroable': 1.0.4 - '@poppinss/utils': 6.9.4 + '@poppinss/utils': 6.10.1 optionalDependencies: + '@adonisjs/lucid': 21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3) '@japa/api-client': 3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0) edge.js: 6.3.0 - '@adonisjs/shield@8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0)': + '@adonisjs/shield@8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0)': dependencies: '@adonisjs/core': 6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0) - '@adonisjs/session': 7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) + '@adonisjs/session': 7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) '@poppinss/utils': 6.9.2 csrf: 3.1.0 helmet-csp: 3.4.0 @@ -5218,16 +5343,16 @@ snapshots: '@adonisjs/tsconfig@1.4.1': {} - '@adonisjs/vite@4.0.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/shield@8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(edge.js@6.3.0)(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': + '@adonisjs/vite@4.0.0(be3e457b6044c7bb333bb5ee47a41fa1)': dependencies: '@adonisjs/core': 6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0) '@poppinss/utils': 6.9.2 - '@vavite/multibuild': 5.1.0(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) + '@vavite/multibuild': 5.1.0(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) edge-error: 4.0.2 - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) - vite-plugin-restart: 0.4.2(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite-plugin-restart: 0.4.2(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) optionalDependencies: - '@adonisjs/shield': 8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.5.1(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) + '@adonisjs/shield': 8.2.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/i18n@2.2.3(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(edge.js@6.3.0))(@adonisjs/session@7.6.0(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@adonisjs/lucid@21.8.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@adonisjs/core@6.19.1(@adonisjs/assembler@7.8.2(typescript@5.7.3))(@vinejs/vine@4.1.0)(argon2@0.43.1)(edge.js@6.3.0))(@vinejs/vine@4.1.0)(luxon@3.7.2)(pg@8.16.3))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0))(@japa/api-client@3.1.0(@japa/assert@4.1.1(@japa/runner@4.4.0))(@japa/runner@4.4.0))(edge.js@6.3.0) edge.js: 6.3.0 '@algolia/autocomplete-core@1.17.9(@algolia/client-search@5.20.4)(algoliasearch@5.20.4)(search-insights@2.17.3)': @@ -5343,7 +5468,7 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.3.0 - tinyexec: 1.0.1 + tinyexec: 1.0.2 '@arr/every@1.0.1': {} @@ -5417,7 +5542,7 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/cli@2.29.8(@types/node@22.18.13)': + '@changesets/cli@2.29.8(@types/node@22.19.2)': dependencies: '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 @@ -5433,7 +5558,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.2(@types/node@22.18.13) + '@inquirer/external-editor': 1.0.2(@types/node@22.19.2) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -5670,9 +5795,9 @@ snapshots: '@esbuild/win32-x64@0.25.11': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': dependencies: - eslint: 9.39.0 + eslint: 9.39.1 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -5711,7 +5836,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.0': {} + '@eslint/js@9.39.1': {} '@eslint/object-schema@2.1.7': {} @@ -5772,12 +5897,12 @@ snapshots: '@iconify/types@2.0.0': {} - '@inquirer/external-editor@1.0.2(@types/node@22.18.13)': + '@inquirer/external-editor@1.0.2(@types/node@22.19.2)': dependencies: chardet: 2.1.0 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 22.18.13 + '@types/node': 22.19.2 '@isaacs/balanced-match@4.0.1': {} @@ -5802,9 +5927,9 @@ snapshots: '@poppinss/hooks': 7.3.0 '@poppinss/macroable': 1.1.0 '@types/superagent': 8.1.9 - cookie: 1.0.2 - set-cookie-parser: 2.7.1 - superagent: 10.2.1 + cookie: 1.1.1 + set-cookie-parser: 2.7.2 + superagent: 10.2.3 optionalDependencies: '@japa/assert': 4.1.1(@japa/runner@4.4.0) transitivePeerDependencies: @@ -5908,6 +6033,9 @@ snapshots: '@noble/hashes@1.7.1': {} + '@noble/hashes@1.8.0': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5920,16 +6048,21 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.0 - '@optique/core@0.7.0': {} + '@optique/core@0.8.0': {} - '@optique/run@0.7.0': + '@optique/run@0.8.0': dependencies: - '@optique/core': 0.7.0 + '@optique/core': 0.8.0 '@paralleldrive/cuid2@2.2.2': dependencies: '@noble/hashes': 1.7.1 + '@paralleldrive/cuid2@2.3.1': + dependencies: + '@noble/hashes': 1.8.0 + optional: true + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -6051,8 +6184,6 @@ snapshots: '@poppinss/exception@1.2.0': {} - '@poppinss/exception@1.2.1': {} - '@poppinss/exception@1.2.2': {} '@poppinss/hooks@7.2.5': {} @@ -6132,15 +6263,6 @@ snapshots: safe-stable-stringify: 2.5.0 secure-json-parse: 3.0.2 - '@poppinss/utils@6.9.4': - dependencies: - '@poppinss/exception': 1.2.1 - '@poppinss/object-builder': 1.1.0 - '@poppinss/string': 1.7.0 - flattie: 1.1.1 - safe-stable-stringify: 2.5.0 - secure-json-parse: 4.1.0 - '@poppinss/utils@7.0.0-next.3': dependencies: '@poppinss/exception': 1.2.2 @@ -6156,69 +6278,135 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.52.5': optional: true + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + '@rollup/rollup-android-arm64@4.52.5': optional: true + '@rollup/rollup-android-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-arm64@4.52.5': optional: true + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-x64@4.52.5': optional: true + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + '@rollup/rollup-freebsd-x64@4.52.5': optional: true + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@shikijs/core@3.1.0': @@ -6287,11 +6475,11 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@stylistic/eslint-plugin@5.5.0(eslint@9.39.0)': + '@stylistic/eslint-plugin@5.5.0(eslint@9.39.1)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@typescript-eslint/types': 8.46.2 - eslint: 9.39.0 + eslint: 9.39.1 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -6434,7 +6622,7 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@22.18.13': + '@types/node@22.19.2': dependencies: undici-types: 6.21.0 @@ -6444,7 +6632,7 @@ snapshots: '@types/proxy-addr@2.0.3': dependencies: - '@types/node': 22.18.13 + '@types/node': 22.19.2 '@types/punycode@2.1.4': {} @@ -6452,7 +6640,7 @@ snapshots: '@types/readdir-glob@1.1.5': dependencies: - '@types/node': 22.18.13 + '@types/node': 22.19.2 '@types/sinon@17.0.4': dependencies: @@ -6464,8 +6652,8 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.18.13 - form-data: 4.0.2 + '@types/node': 22.19.2 + form-data: 4.0.5 optional: true '@types/unist@3.0.3': {} @@ -6474,15 +6662,15 @@ snapshots: '@types/web-bluetooth@0.0.21': {} - '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0)(typescript@5.7.3))(eslint@9.39.0)(typescript@5.7.3)': + '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.1)(typescript@5.7.3))(eslint@9.39.1)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0)(typescript@5.7.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.39.1)(typescript@5.7.3) '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0)(typescript@5.7.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0)(typescript@5.7.3) + '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.1)(typescript@5.7.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.39.1)(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.46.2 - eslint: 9.39.0 + eslint: 9.39.1 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -6491,14 +6679,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.2(eslint@9.39.0)(typescript@5.7.3)': + '@typescript-eslint/parser@8.46.2(eslint@9.39.1)(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 8.46.2 '@typescript-eslint/types': 8.46.2 '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.7.3) '@typescript-eslint/visitor-keys': 8.46.2 debug: 4.4.3 - eslint: 9.39.0 + eslint: 9.39.1 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -6521,13 +6709,13 @@ snapshots: dependencies: typescript: 5.7.3 - '@typescript-eslint/type-utils@8.46.2(eslint@9.39.0)(typescript@5.7.3)': + '@typescript-eslint/type-utils@8.46.2(eslint@9.39.1)(typescript@5.7.3)': dependencies: '@typescript-eslint/types': 8.46.2 '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.7.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0)(typescript@5.7.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.39.1)(typescript@5.7.3) debug: 4.4.3 - eslint: 9.39.0 + eslint: 9.39.1 ts-api-utils: 2.1.0(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: @@ -6551,13 +6739,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.2(eslint@9.39.0)(typescript@5.7.3)': + '@typescript-eslint/utils@8.46.2(eslint@9.39.1)(typescript@5.7.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@typescript-eslint/scope-manager': 8.46.2 '@typescript-eslint/types': 8.46.2 '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.7.3) - eslint: 9.39.0 + eslint: 9.39.1 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -6569,12 +6757,12 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vavite/multibuild@5.1.0(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': + '@vavite/multibuild@5.1.0(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': dependencies: '@types/node': 18.19.130 cac: 6.7.14 picocolors: 1.1.1 - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) '@vinejs/compiler@4.1.1': {} @@ -6590,48 +6778,48 @@ snapshots: normalize-url: 8.1.0 validator: 13.15.20 - '@vitejs/plugin-vue@5.2.1(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))(vue@3.5.13(typescript@5.7.3))': + '@vitejs/plugin-vue@5.2.1(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))(vue@3.5.13(typescript@5.7.3))': dependencies: - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) vue: 3.5.13(typescript@5.7.3) - '@vitest/expect@4.0.14': + '@vitest/expect@4.0.15': dependencies: '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.14(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': + '@vitest/mocker@4.0.15(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.0.15 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) - '@vitest/pretty-format@4.0.14': + '@vitest/pretty-format@4.0.15': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.14': + '@vitest/runner@4.0.15': dependencies: - '@vitest/utils': 4.0.14 + '@vitest/utils': 4.0.15 pathe: 2.0.3 - '@vitest/snapshot@4.0.14': + '@vitest/snapshot@4.0.15': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.15 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.14': {} + '@vitest/spy@4.0.15': {} - '@vitest/utils@4.0.14': + '@vitest/utils@4.0.15': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.0.15 tinyrainbow: 3.0.3 '@vue/compiler-core@3.5.13': @@ -6655,7 +6843,7 @@ snapshots: '@vue/compiler-ssr': 3.5.13 '@vue/shared': 3.5.13 estree-walker: 2.0.2 - magic-string: 0.30.17 + magic-string: 0.30.21 postcss: 8.5.6 source-map-js: 1.2.1 @@ -7110,6 +7298,9 @@ snapshots: cookie@1.0.2: {} + cookie@1.1.1: + optional: true + cookiejar@2.1.4: optional: true @@ -7377,29 +7568,29 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.39.0): + eslint-config-prettier@10.1.8(eslint@9.39.1): dependencies: - eslint: 9.39.0 + eslint: 9.39.1 - eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.0))(eslint@9.39.0)(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1))(eslint@9.39.1)(prettier@3.7.4): dependencies: - eslint: 9.39.0 - prettier: 3.6.2 + eslint: 9.39.1 + prettier: 3.7.4 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@9.39.0) + eslint-config-prettier: 10.1.8(eslint@9.39.1) - eslint-plugin-unicorn@60.0.0(eslint@9.39.0): + eslint-plugin-unicorn@60.0.0(eslint@9.39.1): dependencies: '@babel/helper-validator-identifier': 7.27.1 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@eslint/plugin-kit': 0.3.5 change-case: 5.4.4 ci-info: 4.3.1 clean-regexp: 1.0.0 core-js-compat: 3.46.0 - eslint: 9.39.0 + eslint: 9.39.1 esquery: 1.6.0 find-up-simple: 1.0.1 globals: 16.4.0 @@ -7421,15 +7612,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.0: + eslint@9.39.1: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.0 + '@eslint/js': 9.39.1 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 @@ -7519,7 +7710,7 @@ snapshots: extendable-error@0.1.7: {} - fast-copy@3.0.2: {} + fast-copy@4.0.1: {} fast-deep-equal@3.1.3: {} @@ -7615,17 +7806,18 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.2: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 optional: true formidable@3.5.4: dependencies: - '@paralleldrive/cuid2': 2.2.2 + '@paralleldrive/cuid2': 2.3.1 dezalgo: 1.0.4 once: 1.4.0 optional: true @@ -8090,10 +8282,6 @@ snapshots: luxon@3.7.2: {} - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8443,17 +8631,21 @@ snapshots: dependencies: split2: 4.2.0 - pino-pretty@13.1.2: + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.3: dependencies: colorette: 2.0.20 dateformat: 4.6.3 - fast-copy: 3.0.2 + fast-copy: 4.0.1 fast-safe-stringify: 2.1.1 help-me: 5.0.0 joycon: 3.1.1 minimist: 1.2.8 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 + pino-abstract-transport: 3.0.0 pump: 3.0.2 secure-json-parse: 4.1.0 sonic-boom: 4.2.0 @@ -8509,12 +8701,12 @@ snapshots: dependencies: css: 3.0.0 edgejs-parser: 0.2.15 - prettier: 3.6.2 + prettier: 3.7.4 uglify-js: 3.19.3 prettier@2.8.8: {} - prettier@3.6.2: {} + prettier@3.7.4: {} pretty-format@30.2.0: dependencies: @@ -8710,6 +8902,34 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -8864,6 +9084,9 @@ snapshots: set-cookie-parser@2.7.1: {} + set-cookie-parser@2.7.2: + optional: true + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} @@ -9077,13 +9300,13 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 7.0.0 - superagent@10.2.1: + superagent@10.2.3: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 debug: 4.4.3 fast-safe-stringify: 2.1.1 - form-data: 4.0.2 + form-data: 4.0.5 formidable: 3.5.4 methods: 1.1.2 mime: 2.6.0 @@ -9167,7 +9390,7 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.0.1: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -9204,14 +9427,14 @@ snapshots: '@ts-morph/common': 0.24.0 code-block-writer: 13.0.3 - ts-node-maintained@10.9.6(@swc/core@1.10.18)(@types/node@22.18.13)(typescript@5.7.3): + ts-node-maintained@10.9.6(@swc/core@1.10.18)(@types/node@22.19.2)(typescript@5.7.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.18.13 + '@types/node': 22.19.2 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -9244,13 +9467,13 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.1 - typescript-eslint@8.46.2(eslint@9.39.0)(typescript@5.7.3): + typescript-eslint@8.46.2(eslint@9.39.1)(typescript@5.7.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0)(typescript@5.7.3))(eslint@9.39.0)(typescript@5.7.3) - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0)(typescript@5.7.3) + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.1)(typescript@5.7.3))(eslint@9.39.1)(typescript@5.7.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.39.1)(typescript@5.7.3) '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.7.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0)(typescript@5.7.3) - eslint: 9.39.0 + '@typescript-eslint/utils': 8.46.2(eslint@9.39.1)(typescript@5.7.3) + eslint: 9.39.1 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -9347,12 +9570,12 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-plugin-restart@0.4.2(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)): + vite-plugin-restart@0.4.2(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)): dependencies: micromatch: 4.0.8 - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) - vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1): + vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -9361,13 +9584,13 @@ snapshots: rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.18.13 + '@types/node': 22.19.2 fsevents: 2.3.3 sass: 1.93.3 sass-embedded: 1.93.3 yaml: 2.8.1 - vitepress@2.0.0-alpha.3(@algolia/client-search@5.20.4)(@types/node@22.18.13)(change-case@5.4.4)(postcss@8.5.6)(sass-embedded@1.93.3)(sass@1.93.3)(search-insights@2.17.3)(typescript@5.7.3)(yaml@2.8.1): + vitepress@2.0.0-alpha.3(@algolia/client-search@5.20.4)(@types/node@22.19.2)(change-case@5.4.4)(postcss@8.5.6)(sass-embedded@1.93.3)(sass@1.93.3)(search-insights@2.17.3)(typescript@5.7.3)(yaml@2.8.1): dependencies: '@docsearch/css': 3.9.0 '@docsearch/js': 3.9.0(@algolia/client-search@5.20.4)(search-insights@2.17.3) @@ -9375,7 +9598,7 @@ snapshots: '@shikijs/core': 3.1.0 '@shikijs/transformers': 3.1.0 '@shikijs/types': 3.1.0 - '@vitejs/plugin-vue': 5.2.1(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))(vue@3.5.13(typescript@5.7.3)) + '@vitejs/plugin-vue': 5.2.1(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1))(vue@3.5.13(typescript@5.7.3)) '@vue/devtools-api': 7.7.2 '@vue/shared': 3.5.13 '@vueuse/core': 12.8.2(typescript@5.7.3) @@ -9384,7 +9607,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.1.2 shiki: 3.1.0 - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) vue: 3.5.13(typescript@5.7.3) optionalDependencies: postcss: 8.5.6 @@ -9418,15 +9641,15 @@ snapshots: - universal-cookie - yaml - vitest@4.0.14(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1): + vitest@4.0.15(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1): dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 es-module-lexer: 1.7.0 expect-type: 1.2.2 magic-string: 0.30.21 @@ -9435,13 +9658,13 @@ snapshots: picomatch: 4.0.3 std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 6.4.1(@types/node@22.18.13)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) + vite: 6.4.1(@types/node@22.19.2)(sass-embedded@1.93.3)(sass@1.93.3)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.18.13 + '@types/node': 22.19.2 transitivePeerDependencies: - jiti - less From 2f90adcab5266d94a72ac4aeb7ef680a88ff9402 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 17 Dec 2025 18:30:30 +0100 Subject: [PATCH 12/14] Fix failing nodeinfo test --- components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts index a46c407..7f410d0 100644 --- a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts +++ b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts @@ -8,7 +8,7 @@ import { describe("NodeInfo", () => { test("GET /.well-known/nodeinfo returns JSON", async () => { - const response = await fetch(`${getServerUrl()}/.well-known/nodeinfo`); + const response = await fetch(`${getServerUrl()}.well-known/nodeinfo`); expect(response.status).toBe(200); expect(response.headers.get("content-type")).toContain("application/json"); From 5955f95bd8daabda320b7e6474099f55083b437f Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Sun, 28 Dec 2025 12:25:12 +0100 Subject: [PATCH 13/14] Use URL constructor properly --- components/conformance/src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/conformance/src/utils.ts b/components/conformance/src/utils.ts index 68e146e..4804d77 100644 --- a/components/conformance/src/utils.ts +++ b/components/conformance/src/utils.ts @@ -26,7 +26,8 @@ interface NodeInfo { * Fetches the NodeInfo discovery document from /.well-known/nodeinfo */ export async function getNodeInfoDiscovery(): Promise { - const response = await fetch(`${getServerUrl()}.well-known/nodeinfo`); + const url = new URL(".well-known/nodeinfo", getServerUrl()).href; + const response = await fetch(url); return (await response.json()) as NodeInfoDiscovery; } From 3eb94725c4ddd7afabe28c261ce881ad6c8ab10c Mon Sep 17 00:00:00 2001 From: Steven Langbroek Date: Sun, 28 Dec 2025 14:58:31 +0100 Subject: [PATCH 14/14] Clean up last manual string concatenation in favor of URL construction. --- components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts index 7f410d0..d044e37 100644 --- a/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts +++ b/components/conformance/src/tests/nodeinfo/nodeinfo.spec.ts @@ -8,7 +8,9 @@ import { describe("NodeInfo", () => { test("GET /.well-known/nodeinfo returns JSON", async () => { - const response = await fetch(`${getServerUrl()}.well-known/nodeinfo`); + const response = await fetch( + new URL(".well-known/nodeinfo", getServerUrl()).href, + ); expect(response.status).toBe(200); expect(response.headers.get("content-type")).toContain("application/json");