diff --git a/.gitattributes b/.gitattributes index 75ca699..d83f84f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -25,5 +25,7 @@ release/stable/v*/artifacts/ios/Package.swift text eol=lf release/stable/v*/artifacts/native/scanly-core.h text eol=lf release/stable/v*/artifacts/android/*.aar binary release/stable/v*/artifacts/npm/*.tgz binary +release/stable/v2.1.0/evidence/** text eol=lf +release/stable/v2.1.0/artifacts/provenance/*.json text eol=lf device-lab/test-targets/sha256.json text eol=lf fixtures/alpha5/external-open-license/pdf417/ZXING-APACHE-2.0.txt text eol=lf diff --git a/.github/workflows/stable-npm-publish.yml b/.github/workflows/stable-npm-publish.yml index c51f47d..8c99fd6 100644 --- a/.github/workflows/stable-npm-publish.yml +++ b/.github/workflows/stable-npm-publish.yml @@ -9,6 +9,11 @@ on: description: Exact signed Stable SemVer tag to publish (for example v2.0.1) required: true type: string + qualify_only: + description: Verify existing OIDC grants without publishing, modifying packages, or creating tags + required: false + type: boolean + default: false permissions: contents: read @@ -19,13 +24,35 @@ concurrency: cancel-in-progress: false jobs: + credential-qualification: + name: Verify existing npm OIDC grants (no publication) + if: github.event_name == 'workflow_dispatch' && inputs.qualify_only + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v7 + with: + node-version: 24 + - name: Exchange existing trusted identities without publishing + run: node scripts/probe-stable-npm-oidc.mjs + - uses: actions/upload-artifact@v4 + with: + name: npm-oidc-qualification + path: release-dist/npm-oidc-qualification.json + if-no-files-found: error + retention-days: 90 publish: name: Publish npm packages with provenance if: >- - github.event_name == 'workflow_dispatch' || - (github.event.release.prerelease == false && github.event.release.draft == false) + (github.event_name == 'workflow_dispatch' && !inputs.qualify_only) || + (github.event_name == 'release' && github.event.release.prerelease == false && github.event.release.draft == false) runs-on: ubuntu-latest steps: + - name: Assert publication mode before any release checkout + env: + QUALIFY_ONLY: ${{ inputs.qualify_only }} + run: test "$QUALIFY_ONLY" != "true" - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -132,7 +159,21 @@ jobs: while IFS=$'\t' read -r package tarball; do test -n "$package" -a -f "$tarball" published="$(npm view "${package}@${VERSION}" version --json 2>/dev/null | tr -d '"' || true)" + if [ "$VERSION" = "2.1.0" ] && [ "$package" = "@scanly/url-safety" ] && [ "$published" != "$VERSION" ]; then + node --input-type=commonjs - "$STABLE_RELEASE_ROOT" <<'BOOTSTRAP' + const fs = require('node:fs'); + const path = require('node:path'); + const credentials = JSON.parse(fs.readFileSync(path.join(process.argv[2], 'publication-credentials.json'), 'utf8')); + const bootstrap = credentials.channels.npmRegistry.evidence.bootstrap; + if (bootstrap?.package !== '@scanly/url-safety' || bootstrap.version !== '2.1.0' || bootstrap.method !== 'INTERACTIVE_NPM_PROVENANCE_FILE' || !bootstrap.interactiveTwoFactorRequired || !bootstrap.signedReleaseTagRequired || !bootstrap.provenanceVerified) throw new Error('No qualified first-publication bootstrap'); + fs.writeFileSync('stable-npm-bootstrap-pending.txt', '@scanly/url-safety@2.1.0 requires interactive publication with its qualified CI provenance bundle. No release completion is claimed.\n'); + BOOTSTRAP + echo "Deferring the explicitly qualified new package; publishing its dependencies first." + continue + fi if [ "$published" = "$VERSION" ]; then + expected_integrity="$(node -e 'const fs=require("fs"),c=require("crypto"); process.stdout.write("sha512-"+c.createHash("sha512").update(fs.readFileSync(process.argv[1])).digest("base64"))' "$tarball")" + test "$(npm view "${package}@${VERSION}" dist.integrity --json | tr -d '"')" = "$expected_integrity" echo "${package}@${VERSION} already exists; verifying and continuing" elif [ "$VERSION" = "2.0.0" ]; then # Legacy v2.0.0 recovery only: immutable tarballs predate npm's @@ -187,6 +228,7 @@ jobs: for attempt in {1..60}; do pending=0 while IFS=$'\t' read -r package tarball; do + if [ -f stable-npm-bootstrap-pending.txt ] && [ "$package" = "@scanly/url-safety" ]; then continue; fi verified="$(npm view "${package}@${VERSION}" version --json 2>/dev/null | tr -d '"' || true)" if [ "$verified" != "$VERSION" ]; then echo "Waiting for Registry propagation: ${package}@${VERSION} (${attempt}/60)" @@ -199,6 +241,16 @@ jobs: test "$pending" -eq 0 while IFS=$'\t' read -r package tarball; do + if [ -f stable-npm-bootstrap-pending.txt ] && [ "$package" = "@scanly/url-safety" ]; then continue; fi test "$(npm view "$package" dist-tags.latest --json | tr -d '"')" = "$VERSION" test "$(npm view "${package}@${VERSION}" dist.attestations.provenance.predicateType --json | tr -d '"')" = "https://slsa.dev/provenance/v1" done < stable-npm-publication-plan.tsv + if [ -f stable-npm-bootstrap-pending.txt ]; then + echo "::notice title=Publication incomplete::Existing packages are verified; the new URL Safety package still requires its approved interactive bootstrap." + fi + - uses: actions/upload-artifact@v4 + if: always() + with: + name: stable-npm-bootstrap-status + path: stable-npm-bootstrap-pending.txt + if-no-files-found: ignore diff --git a/.github/workflows/v2.1-artifact-qualification.yml b/.github/workflows/v2.1-artifact-qualification.yml new file mode 100644 index 0000000..f5f41c6 --- /dev/null +++ b/.github/workflows/v2.1-artifact-qualification.yml @@ -0,0 +1,81 @@ +name: v2.1 Artifact Qualification + +on: + pull_request: + branches: [develop] + paths: + - .github/workflows/v2.1-artifact-qualification.yml + - scripts/prepare-v2.1-npm-artifacts.mjs + workflow_dispatch: + inputs: + source_commit: + description: Exact qualified develop product source (40-character commit) + required: true + type: string + +permissions: + contents: read + id-token: write + +jobs: + clean-build: + if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository + name: Clean npm build ${{ matrix.build }} + runs-on: ubuntu-latest + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + build: [A, B] + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + path: tools + fetch-depth: 0 + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.source_commit || github.event.pull_request.base.sha }} + path: product + fetch-depth: 0 + - uses: actions/setup-node@v7 + with: + node-version: 24 + - name: Install pinned npm + run: npm install --global npm@11.12.1 + - name: Verify source identity and install without dependency cache + working-directory: product + env: + SOURCE_COMMIT: ${{ inputs.source_commit || github.event.pull_request.base.sha }} + run: | + set -euo pipefail + [[ "$SOURCE_COMMIT" =~ ^[a-f0-9]{40}$ ]] + test "$(git rev-parse HEAD)" = "$SOURCE_COMMIT" + test -z "${NODE_AUTH_TOKEN:-}" + test -z "${NPM_TOKEN:-}" + npm ci + - name: Build and verify public packages + working-directory: product + run: | + npm run build:packages + npm run api:snapshot + npm run native:api + npm run native:abi + npm run packages:smoke + npm run packages:tarball + npm run url-safety:security + git diff --exit-code -- packages engines package-lock.json api-snapshots + - name: Pack exact artifacts and sign detached provenance (no publication) + working-directory: product + env: + SOURCE_COMMIT: ${{ inputs.source_commit || github.event.pull_request.base.sha }} + BUILD_LABEL: ${{ matrix.build }} + run: | + export NPM_CLI_PATH="$(readlink -f "$(command -v npm)")" + node ../tools/scripts/prepare-v2.1-npm-artifacts.mjs --root=. --output=../artifacts + - uses: actions/upload-artifact@v4 + with: + name: v2.1-npm-${{ matrix.build }} + path: artifacts + if-no-files-found: error + retention-days: 90 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3411b4f..355bf0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,13 @@ All notable changes follow semantic versioning. -## Unreleased - -## 2.1.0 — Link Intelligence (development; qualification pending) +## 2.1.0 — Link Intelligence - Added optional `@scanly/url-safety` with local URL signals, canonicalization, redaction, cache, cancellation, confirmed-event integration and deterministic scoring. - Added a Node-only server entry with pinned public-IP connections, per-hop SSRF validation, bounded inspection, reputation lookup adapters and schema-validated advisory LLM evidence. - Added explicit privacy modes and a default-disabled demo, with a same-origin rate/size/time-limited backend. - Existing barcode decoding remains local and independent of network analysis. Published 2.0.0 and 2.0.1 artifacts and historical evidence are unchanged. -- Release status and unresolved qualification requirements are recorded in `docs/releases/v2.1.0.md`; this entry does not assert publication. +- Added source-bound independent npm and Native artifact rebuilds, detached CI provenance for the new package's 2FA-protected first publication, and immutable versioned qualification records. Actual publication is verified separately from qualification. ## 2.0.1 — Scanner lifecycle and runtime hardening diff --git a/README.md b/README.md index 14a07e8..ef3817e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -# Scanly SDK v2.1.0 — Link Intelligence (development) +# Scanly SDK v2.1.0 — Link Intelligence Privacy-first, local-only barcode scanning SDK for Web, Node.js, React, iOS, and Android. -[![SDK 2.0.1](https://img.shields.io/badge/SDK-2.0.1-green)](https://github.com/Yangjunjie-Lin/Scanly/releases/tag/v2.0.1) -[![Source 2.1.0](https://img.shields.io/badge/SDK-2.1.0-green)](docs/releases/v2.1.0.md) +[![SDK 2.1.0](https://img.shields.io/badge/SDK-2.1.0-green)](docs/releases/v2.1.0.md) [![npm latest](https://img.shields.io/badge/npm-latest-CB3837)](https://www.npmjs.com/package/@scanly/browser) [![TypeScript](https://img.shields.io/badge/TypeScript-5-blue)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) @@ -14,7 +13,7 @@ Privacy-first, local-only barcode scanning SDK for Web, Node.js, React, iOS, and ## Install -This branch develops 2.1.0. The published Stable release remains 2.0.1 until the new release qualification and publication gates pass. +The 2.1.0 release line is software-qualified. Registry publication is finalized separately through the signed release workflow; see the [release notes](docs/releases/v2.1.0.md) and versioned release records below. Choose the package for your runtime. Browser, Node, and React packages already include their required Scanly workspace dependencies. @@ -50,8 +49,8 @@ Advanced engine composition can use `@scanly/core` directly: npm install @scanly/core ``` -- **iOS:** v2.0.1 ships a Swift Package Manager source package under `native/ios`. Because the manifest is not at the repository root, check out tag `v2.0.1` and add `native/ios` as a local package. See the [iOS guide](docs/native/ios-getting-started.md). -- **Android:** download `scanly-sdk-2.0.1.aar` from the [v2.0.1 GitHub Release](https://github.com/Yangjunjie-Lin/Scanly/releases/tag/v2.0.1). Maven Central is not a v2.0.1 distribution channel. See the [Android guide](docs/native/android-getting-started.md). +- **iOS:** v2.1.0 provides a Swift Package Manager source package under `native/ios`. Because the manifest is not at the repository root, check out tag `v2.1.0` and add `native/ios` as a local package. See the [iOS guide](docs/native/ios-getting-started.md). +- **Android:** use `scanly-sdk-2.1.0.aar` from the [v2.1.0 GitHub Release](https://github.com/Yangjunjie-Lin/Scanly/releases/tag/v2.1.0). Maven Central is not a v2.1.0 distribution channel. See the [Android guide](docs/native/android-getting-started.md). ## Quick start @@ -149,19 +148,19 @@ Industrial recovery is not an industrial, warehouse, or DPM certification. The ` ## Privacy -Scanly decodes locally and remains offline-capable after code and WASM assets are loaded. The SDK does not upload images or payloads and contains no analytics, remote logging, account, or cloud-decoder service. Host applications remain responsible for their own telemetry, storage, and privacy disclosures. +Barcode decoding remains local and offline-capable after code and WASM assets are loaded. No scanning image or camera frame is uploaded. The separate URL intelligence module is disabled by default and shares decoded URLs or sanitized page evidence only after explicit opt-in. There is no built-in analytics or remote logging. Host applications remain responsible for their own telemetry, storage, service configuration and privacy disclosures. ## Platform status -v2.0.1 is the current Stable software line. It hardens cancellation, React concurrency, camera error handling, and the terminal lifecycle of `ScannerSession`. Automated browser, Node, simulator, emulator, API/ABI, security, package, and artifact checks are verified. Automated coverage is not physical-device qualification. +v2.1.0 adds opt-in Link Intelligence on top of the v2.0.1 cancellation, React concurrency, camera error handling and terminal `ScannerSession` lifecycle guarantees. Automated browser, Node, simulator, emulator, API/ABI, security, package and artifact checks are verified. Automated coverage is not physical-device qualification. Physical Web iOS/Android, Native iOS/Android, 30-minute and 60-minute camera soaks, and the complete hardware matrix remain `POST_RELEASE_VALIDATION_PENDING` under [Issue #13](https://github.com/Yangjunjie-Lin/Scanly/issues/13). No all-device-verified claim is made. -Known limitations include hardware-dependent camera capabilities, difficult or occluded symbols, experimental DPM recovery, unsupported Android `armeabi-v7a`, no Maven Central or CocoaPods distribution for v2.0.1, and the pending physical-device matrix. +Known limitations include hardware-dependent camera capabilities, difficult or occluded symbols, experimental DPM recovery, unsupported Android `armeabi-v7a`, no Maven Central or CocoaPods distribution for v2.1.0, and the pending physical-device matrix. URL risk verdicts are advisory observations, not safety guarantees. ## Branch and release status -- `main` contains the current v2.0.1 Stable line. +- `main` is the Stable release branch; immutable versioned publication records identify completed releases. - `develop` is the integration branch for post-v2 maintenance and the next patch/minor line. - Feature and fix branches start from `develop`. - Immutable RC/Stable tags and versioned evidence preserve qualification ancestry. Only `main` and `develop` are retained as long-lived branches. @@ -180,6 +179,10 @@ Detailed Alpha, Beta, and RC development evidence is preserved in [development h ## Release integrity +- [v2.1.0 release notes](docs/releases/v2.1.0.md) +- [v2.1.0 qualification manifest](release/stable/v2.1.0/v2.1.0-manifest.json) +- [v2.1.0 checksums](release/stable/v2.1.0/checksums.sha256) +- [v2.1.0 SBOM](release/stable/v2.1.0/sbom.cdx.json) - [v2.0.1 release notes](docs/releases/v2.0.1.md) - [v2.0.1 qualification manifest](release/stable/v2.0.1/v2.0.1-manifest.json) - [v2.0.1 checksums](release/stable/v2.0.1/checksums.sha256) diff --git a/docs/releases/v2.1.0.md b/docs/releases/v2.1.0.md index 5304d75..5f814b2 100644 --- a/docs/releases/v2.1.0.md +++ b/docs/releases/v2.1.0.md @@ -1,6 +1,6 @@ # Scanly SDK v2.1.0 -Status: development candidate; qualification and publication pending. Published Stable remains v2.0.1. This document is not a publication record. +Status: release-qualified candidate. Registry publication and post-publication verification are tracked separately; this document is not a publication record. ## New: Link Intelligence @@ -28,4 +28,6 @@ Existing v2 scanner APIs remain compatible. URL intelligence is an independent, All software, browser, API/package, security, Native, SBOM/license, artifact/reproducibility, signing and publication gates must pass on the final release identity. Feature-branch test results are development evidence only. See [URL safety architecture](../url-safety.md) and the candidate qualification record when available. Issue #13 remains independent. -The Stable manifest generator refuses new-version generation without `STABLE_QUALIFICATION_RECORD`: an exact-source `scanly-release-qualification-1` record with version, sourceCommit, sourceTree and content-addressed PASS evidence (`path`, `sha256`, `sourceCommit`) for software, apiAbi, security, sbom, licenses, native, npmReproducibility, signing, publicationCredentials and deployment. Fresh public signing metadata, 11-package Trusted Publishing configuration and two independent npm build/hash maps are required. Legacy 2.0.x GO/credential/reproducibility claims cannot be inherited. +The Stable manifest generator refuses new-version generation without `STABLE_QUALIFICATION_RECORD`: an exact-source `scanly-release-qualification-1` record with version, sourceCommit, sourceTree and content-addressed PASS evidence (`path`, `sha256`, `sourceCommit`) for software, apiAbi, security, sbom, licenses, native, npmReproducibility, signing, publicationCredentials and deployment. Fresh public signing metadata and two independent npm build/hash maps are required. Legacy 2.0.x GO/credential/reproducibility claims cannot be inherited. + +For the new `@scanly/url-safety` package only, the qualified first-publication path uses npm's official `--provenance-file` support: exact release tarball bytes carry a GitHub-hosted build's verified Sigstore provenance, while an authenticated owner completes npm's interactive 2FA locally. No npm credential is sent to CI, no placeholder version is published, and no provenance or signing requirement is waived. Existing packages continue using their verified OIDC workflow. Dependencies publish before the new package; a deferred bootstrap is reported as incomplete, never as full release success. The new package's Trusted Publisher can be initialized after its first genuine release. diff --git a/package.json b/package.json index b15c0af..89376bf 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "rc:manifest:verify": "node scripts/verify-release-manifest.mjs", "stable:manifest:verify": "node scripts/verify-stable-manifest.mjs", "stable:publication:verify": "node scripts/verify-stable-publication-record.mjs", + "stable:bootstrap": "node scripts/publish-v2.1-bootstrap.mjs", "stable:manifest:generate": "node scripts/generate-stable-release-manifest.mjs", "stable:android:record": "node scripts/record-stable-android-artifact.mjs", "stable:reproducibility:confirm": "node scripts/confirm-stable-metadata-reproducibility.mjs", diff --git a/release/stable/v2.1.0/android-build-evidence.json b/release/stable/v2.1.0/android-build-evidence.json new file mode 100644 index 0000000..b0db267 --- /dev/null +++ b/release/stable/v2.1.0/android-build-evidence.json @@ -0,0 +1,108 @@ +{ + "schemaVersion": "scanly-stable-android-build-evidence-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "Native Mobile", + "workflowDefinitionCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "requestedCheckoutCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "cleanBuildA": { + "runId": 34485084420, + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420", + "conclusion": "SUCCESS", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "size": 1221160 + }, + "cleanBuildB": { + "runId": 34721927940, + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940", + "conclusion": "SUCCESS", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "size": 1221160 + }, + "rawBuildAEqualsBuildB": true, + "normalizedBuildAEqualsBuildB": true, + "selectedArtifact": { + "build": "A", + "path": "release/stable/v2.1.0/artifacts/android/scanly-sdk-2.1.0.aar", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "size": 1221160 + }, + "canonicalization": { + "schemaVersion": "scanly-zip-content-canonical-1", + "archiveParser": "NODE_ZIP_CENTRAL_DIRECTORY_VALIDATED", + "archiveMetadata": "EXCLUDED", + "entryOrder": "LEXICOGRAPHIC_UTF8_PATH", + "nestedJarMetadata": "EXCLUDED_RECURSIVELY", + "fileContent": "EXACT_BYTES", + "framing": "PATH_NUL_DECIMAL_LENGTH_NUL_CONTENT_NUL" + }, + "validation": { + "aarContents": "PASS", + "androidManifest": "PASS", + "consumerRules": "PASS", + "publicApiClasses": "PASS", + "nativeSymbols": "PASS_BY_WORKFLOW", + "emulatorX8664SharedFixtureSmoke": "PASS_BY_WORKFLOW", + "abis": { + "arm64-v8a": "PASS", + "x86_64": "PASS", + "armeabi-v7a": "UNSUPPORTED" + } + }, + "physicalExecution": "POST_RELEASE_VALIDATION_PENDING", + "status": "GO", + "metadataCleanBuilds": { + "status": "GO", + "cleanBuildAEqualsBuildB": true, + "generator": "scripts/generate-stable-release-manifest.mjs", + "comparison": "EXACT_FILE_BYTES_MATCH", + "comparedFiles": [ + "artifact-manifest.json", + "sbom.cdx.json", + "license-inventory.json", + "physical-validation-status.json", + "release-policy.json", + "signing-manifest.json", + "publication-credentials.json", + "deployment.json", + "reproducibility.json", + "v2.1.0-manifest.json", + "v2.1.0-manifest.json.sha256", + "checksums.sha256", + "artifacts/ios/Package.swift", + "artifacts/native/scanly-core.h", + "qualification-input.json", + "artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "evidence/allowed-signers", + "evidence/api-abi.json", + "evidence/deployment-input.json", + "evidence/native-build-a.json", + "evidence/native-build-b.json", + "evidence/npm-build-run.json", + "evidence/npm-oidc-qualification.json", + "evidence/npm-reproducibility.json", + "evidence/publication-credentials-input.json", + "evidence/security.json", + "evidence/signing-probe.tag", + "evidence/signing.json", + "evidence/software.json", + "evidence/url-safety-benchmark.json", + "evidence/url-safety-tests.json", + "artifacts/npm/scanly-benchmark-2.1.0.tgz", + "artifacts/npm/scanly-browser-2.1.0.tgz", + "artifacts/npm/scanly-core-2.1.0.tgz", + "artifacts/npm/scanly-engine-jsqr-2.1.0.tgz", + "artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz", + "artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz", + "artifacts/npm/scanly-node-2.1.0.tgz", + "artifacts/npm/scanly-parsers-2.1.0.tgz", + "artifacts/npm/scanly-react-2.1.0.tgz", + "artifacts/npm/scanly-scenario-schema-2.1.0.tgz", + "artifacts/npm/scanly-url-safety-2.1.0.tgz" + ] + } +} diff --git a/release/stable/v2.1.0/artifact-manifest.json b/release/stable/v2.1.0/artifact-manifest.json new file mode 100644 index 0000000..114b255 --- /dev/null +++ b/release/stable/v2.1.0/artifact-manifest.json @@ -0,0 +1,237 @@ +{ + "schemaVersion": "scanly-stable-artifact-manifest-1", + "version": "2.1.0", + "productSourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "generatedFrom": "STABLE_SOURCE_COMMIT", + "rawArtifactDigest": { + "algorithm": "SHA-256", + "scope": "EXACT_FILE_BYTES" + }, + "npmCanonicalization": { + "schemaVersion": "scanly-npm-package-canonical-1", + "archiveParser": "NODE_GZIP_POSIX_TAR_VALIDATED", + "archiveMetadata": "EXCLUDED", + "entryOrder": "LEXICOGRAPHIC_UTF8_PATH", + "entryPathPrefix": "package/", + "textDetection": "STRICT_UTF8_WITHOUT_NUL", + "textLineEndings": "CRLF_OR_CR_TO_LF", + "framing": "PATH_NUL_DECIMAL_LENGTH_NUL_CONTENT_NUL" + }, + "artifacts": [ + { + "id": "npm-scanly-benchmark-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-benchmark-2.1.0.tgz", + "sha256": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "size": 12489, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "9bc96773c6ce1ac45a22a7416d1d14b5944942f12149d440382f6029207054d2", + "status": "PASS" + }, + { + "id": "npm-scanly-browser-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-browser-2.1.0.tgz", + "sha256": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "size": 112845, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "0d6bda3c785f74ab114447f8d95b59d54c82b39012a6cacaaca56fd9d6195834", + "status": "PASS" + }, + { + "id": "npm-scanly-core-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-core-2.1.0.tgz", + "sha256": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "size": 133351, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "3327cb8e11fffc695565c483ca68571bbd306b63178be4655c143b906fd966c0", + "status": "PASS" + }, + { + "id": "npm-scanly-engine-jsqr-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-engine-jsqr-2.1.0.tgz", + "sha256": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "size": 2737, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "4901f10928b6d407bb77d8a19fbbe1479466e5097918e0b3b9e5115574b3e588", + "status": "PASS" + }, + { + "id": "npm-scanly-engine-zxing-cpp-wasm-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz", + "sha256": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "size": 472873, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "c2a29ab9bd32dd6abfe6ece44f43b93870a321d2a47c05e092054eafa29fafae", + "status": "PASS" + }, + { + "id": "npm-scanly-engine-zxing-js-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz", + "sha256": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "size": 3188, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "b2db9e1e6f5f3044c5ee70f6a6c80b9e89ddd3508e84da95263306fdeb06575e", + "status": "PASS" + }, + { + "id": "npm-scanly-node-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-node-2.1.0.tgz", + "sha256": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "size": 4163, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "997eed8bbefdf511b8335c1f4ff4270affa2508518b9d9a6b25080b245a7bf16", + "status": "PASS" + }, + { + "id": "npm-scanly-parsers-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-parsers-2.1.0.tgz", + "sha256": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "size": 5415, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "f31ffedeed2acd483bc341429f14ff90a2e937b41656e287087311d2f900b00b", + "status": "PASS" + }, + { + "id": "npm-scanly-react-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-react-2.1.0.tgz", + "sha256": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "size": 2873, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "7ce50eb85bdce1b28bf5d7794976dc475d6c78d3f3206a74ae38e822a851f8fa", + "status": "PASS" + }, + { + "id": "npm-scanly-scenario-schema-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-scenario-schema-2.1.0.tgz", + "sha256": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "size": 10376, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "4b429a8d0256c59458ea8890024252c606e23adf9d86240799e04579d1299cfb", + "status": "PASS" + }, + { + "id": "npm-scanly-url-safety-2.1.0", + "platform": "npm", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", + "sha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "size": 35468, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/npm-pack", + "toolchain": "node v24.15.0; npm >=10", + "canonicalContentSha256": "91fcc49cc0d8aed0d91b520038b6a4ef42eb8a9bd05348573eb05564ed8d7346", + "status": "PASS" + }, + { + "id": "ios-swift-package-source", + "platform": "ios", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/ios/Package.swift", + "sha256": "24d04f5d3a67c55aee95af7b4bf158f359a0c4b4da3f37b7df173269bd9af66f", + "size": 1180, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/ios-source-package", + "toolchain": "Swift Package Manager source manifest", + "status": "PASS_SOURCE_PACKAGE" + }, + { + "id": "native-core-header", + "platform": "native-core", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/native/scanly-core.h", + "sha256": "82dee124d9b88e8ee2c9ea6727a83a3a9e08fb6b70ca753c404928d70624d07a", + "size": 8005, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "stable-artifact-build/native-boundary", + "toolchain": "C++20 public header", + "status": "PASS" + }, + { + "id": "android-aar", + "platform": "android", + "version": "2.1.0", + "path": "release/stable/v2.1.0/artifacts/android/scanly-sdk-2.1.0.aar", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "size": 1221160, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "workflow": "Native Mobile / stable-artifact-build/android-gradle", + "workflowRuns": [ + 34485084420, + 34721927940 + ], + "toolchain": "Gradle 8.13; Android SDK 36; NDK 27.2.12479018; CMake 3.22.1", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "canonicalization": { + "schemaVersion": "scanly-zip-content-canonical-1", + "archiveParser": "NODE_ZIP_CENTRAL_DIRECTORY_VALIDATED", + "archiveMetadata": "EXCLUDED", + "entryOrder": "LEXICOGRAPHIC_UTF8_PATH", + "nestedJarMetadata": "EXCLUDED_RECURSIVELY", + "fileContent": "EXACT_BYTES", + "framing": "PATH_NUL_DECIMAL_LENGTH_NUL_CONTENT_NUL" + }, + "requiredAbis": [ + "arm64-v8a", + "x86_64" + ], + "unsupportedAbis": [ + "armeabi-v7a" + ], + "status": "PASS" + } + ] +} diff --git a/release/stable/v2.1.0/artifacts/android/scanly-sdk-2.1.0.aar b/release/stable/v2.1.0/artifacts/android/scanly-sdk-2.1.0.aar new file mode 100644 index 0000000..f673aea Binary files /dev/null and b/release/stable/v2.1.0/artifacts/android/scanly-sdk-2.1.0.aar differ diff --git a/release/stable/v2.1.0/artifacts/ios/Package.swift b/release/stable/v2.1.0/artifacts/ios/Package.swift new file mode 100644 index 0000000..e2e2870 --- /dev/null +++ b/release/stable/v2.1.0/artifacts/ios/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + // Scanly SDK v2.1.0 Stable; package manifests do not carry semantic versions. + name: "ScanlySDK", + platforms: [.iOS(.v13), .macOS(.v13)], + products: [.library(name: "ScanlySDK", targets: ["ScanlySDK"])], + dependencies: [ + .package(url: "https://github.com/zxing-cpp/zxing-cpp.git", revision: "6c2961d2a9ea4bc4e4ae8f37b1497299f04dd861") + ], + targets: [ + .target( + name: "CScanlyCore", + dependencies: [.product(name: "ZXingCpp", package: "zxing-cpp")], + path: "Core", + sources: ["scanly_core.cpp"], + publicHeadersPath: "include", + cxxSettings: [.define("SCANLY_ENABLE_ZXING_CPP", to: "1")] + ), + .target( + name: "ScanlySDK", + dependencies: ["CScanlyCore"], + path: "Sources/ScanlySDK", + swiftSettings: [.define("SCANLY_STABLE")] + ), + .testTarget( + name: "ScanlySDKTests", + dependencies: ["ScanlySDK", "CScanlyCore"], + path: "Tests/ScanlySDKTests" + ) + ], + cxxLanguageStandard: .cxx20 +) diff --git a/release/stable/v2.1.0/artifacts/native/scanly-core.h b/release/stable/v2.1.0/artifacts/native/scanly-core.h new file mode 100644 index 0000000..a5c4dbd --- /dev/null +++ b/release/stable/v2.1.0/artifacts/native/scanly-core.h @@ -0,0 +1,269 @@ +#ifndef SCANLY_NATIVE_CORE_H +#define SCANLY_NATIVE_CORE_H + +/* + * Scanly Native Core C ABI. + * + * The ABI intentionally exposes only fixed-width values, opaque handles and + * borrowed slices. No C++ standard-library type crosses this boundary. + */ + +#include +#include + +#if defined(_WIN32) && defined(scanly_core_EXPORTS) +# define SCANLY_CORE_API __declspec(dllexport) +#elif defined(_WIN32) && defined(SCANLY_CORE_SHARED) +# define SCANLY_CORE_API __declspec(dllimport) +#elif defined(__GNUC__) || defined(__clang__) +# define SCANLY_CORE_API __attribute__((visibility("default"))) +#else +# define SCANLY_CORE_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SCANLY_ABI_VERSION = 1, + SCANLY_CORE_ABI_VERSION = SCANLY_ABI_VERSION, + SCANLY_CORE_MAX_RESULTS = 64 +}; + +typedef struct scanly_context_t scanly_context_t; +typedef struct scanly_result_set_t scanly_result_set_t; +typedef struct scanly_cancel_token_t scanly_cancel_token_t; + +typedef uint32_t scanly_status_t; +enum { + SCANLY_STATUS_OK = 0, + SCANLY_STATUS_INVALID_INPUT = 1, + SCANLY_STATUS_UNSUPPORTED_FORMAT = 2, + SCANLY_STATUS_DECODE_FAILED = 3, + SCANLY_STATUS_ENGINE_INITIALIZATION_FAILED = 4, + SCANLY_STATUS_ENGINE_EXECUTION_FAILED = 5, + SCANLY_STATUS_CANCELLED = 6, + SCANLY_STATUS_OUT_OF_MEMORY = 7, + SCANLY_STATUS_INTERNAL_ERROR = 8 +}; + +typedef uint32_t scanly_backend_t; +/* Deterministic contract backend. It is not a barcode decoder. */ +enum { + SCANLY_BACKEND_FIXTURE = 1, +/* Requires an externally supplied ZXing-C++ installation at build time. */ + SCANLY_BACKEND_ZXING_CPP = 2 +}; + +typedef uint32_t scanly_pixel_format_t; +enum { + SCANLY_PIXEL_Y = 1, + SCANLY_PIXEL_RGBA = 2, + SCANLY_PIXEL_BGRA = 3 +}; + +/* Public Scanly format vocabulary. Values are stable bit flags. */ +enum { + SCANLY_FORMAT_QR_CODE = 1 << 0, + SCANLY_FORMAT_DATA_MATRIX = 1 << 1, + SCANLY_FORMAT_PDF417 = 1 << 2, + SCANLY_FORMAT_CODE_128 = 1 << 3, + SCANLY_FORMAT_EAN_13 = 1 << 4, + SCANLY_FORMAT_EAN_8 = 1 << 5, + SCANLY_FORMAT_UPC_A = 1 << 6, + SCANLY_FORMAT_UPC_E = 1 << 7, + SCANLY_FORMAT_MASK_ALL = 0xff +}; + +typedef uint32_t scanly_format_class_t; +enum { + SCANLY_FORMAT_CLASS_UNKNOWN = 0, + SCANLY_FORMAT_CLASS_MATRIX = 1, + SCANLY_FORMAT_CLASS_STACKED = 2, + SCANLY_FORMAT_CLASS_LINEAR = 3 +}; + +typedef uint32_t scanly_checksum_status_t; +enum { + SCANLY_CHECKSUM_UNKNOWN = 0, + SCANLY_CHECKSUM_NOT_APPLICABLE = 1, + SCANLY_CHECKSUM_VALID = 2, + SCANLY_CHECKSUM_INVALID = 3 +}; + +typedef uint32_t scanly_engine_status_t; +enum { + SCANLY_ENGINE_SUCCESS = 0, + SCANLY_ENGINE_NOT_FOUND = 1, + SCANLY_ENGINE_UNSUPPORTED = 2, + SCANLY_ENGINE_CANCELLED = 3, + SCANLY_ENGINE_INITIALIZATION_FAILURE = 4, + SCANLY_ENGINE_EXECUTION_FAILURE = 5 +}; + +typedef struct scanly_context_options_t { + uint32_t struct_size; + uint32_t abi_version; + uint32_t backend; + uint32_t reserved[5]; +} scanly_context_options_t; + +typedef struct scanly_image_view_t { + uint32_t struct_size; + const uint8_t *data; + size_t data_len; + uint32_t width; + uint32_t height; + size_t row_stride; + size_t pixel_stride; + uint32_t pixel_format; + uint32_t rotation_degrees; +} scanly_image_view_t; + +typedef struct scanly_decode_options_t { + uint32_t struct_size; + uint32_t abi_version; + uint32_t format_mask; + uint32_t max_results; + uint32_t flags; + const scanly_cancel_token_t *cancel_token; + uint32_t reserved[3]; +} scanly_decode_options_t; + +typedef struct scanly_point_t { + double x; + double y; +} scanly_point_t; + +typedef struct scanly_bbox_t { + double x; + double y; + double width; + double height; +} scanly_bbox_t; + +/* Result flags are additive and may be extended in later ABI versions. */ +enum { + SCANLY_RESULT_FLAG_HAS_ORIENTATION = 1 << 0, + SCANLY_RESULT_FLAG_GS1 = 1 << 1, + SCANLY_RESULT_FLAG_VALIDATED = 1 << 2, + SCANLY_RESULT_FLAG_GEOMETRY_ESTIMATED = 1 << 3 +}; + +typedef struct scanly_result_view_t { + uint32_t struct_size; + uint32_t format; + uint32_t format_class; + uint32_t flags; + uint32_t checksum_status; + uint32_t corner_count; + uint32_t reserved0; + const scanly_point_t *corner_points; + scanly_bbox_t bounding_box; + double orientation_degrees; + const char *payload_utf8; + size_t payload_len; + const uint8_t *raw_bytes; + size_t raw_bytes_len; + const char *symbology_identifier; + size_t symbology_identifier_len; + const char *structured_metadata_json; + size_t structured_metadata_json_len; + const char *engine_id; + size_t engine_id_len; + const char *engine_version; + size_t engine_version_len; + const char *diagnostics_json; + size_t diagnostics_json_len; + uint32_t reserved[4]; +} scanly_result_view_t; + +typedef struct scanly_engine_diagnostic_view_t { + uint32_t struct_size; + uint32_t status; + uint64_t elapsed_us; + uint32_t attempt_count; + uint32_t result_count; + const char *engine_id; + size_t engine_id_len; + const char *engine_version; + size_t engine_version_len; + const char *error_code; + size_t error_code_len; + const char *message; + size_t message_len; + const char *diagnostics_json; + size_t diagnostics_json_len; + uint32_t reserved[4]; +} scanly_engine_diagnostic_view_t; + +SCANLY_CORE_API const char *scanly_core_version(void); +SCANLY_CORE_API const char *scanly_status_message(scanly_status_t status); + +SCANLY_CORE_API void scanly_context_options_init(scanly_context_options_t *options); +SCANLY_CORE_API void scanly_image_view_init(scanly_image_view_t *image); +SCANLY_CORE_API void scanly_decode_options_init(scanly_decode_options_t *options); +SCANLY_CORE_API void scanly_result_view_init(scanly_result_view_t *view); +SCANLY_CORE_API void scanly_engine_diagnostic_view_init(scanly_engine_diagnostic_view_t *view); + +SCANLY_CORE_API scanly_status_t scanly_context_create( + const scanly_context_options_t *options, + scanly_context_t **out_context); +SCANLY_CORE_API void scanly_context_destroy(scanly_context_t *context); + +SCANLY_CORE_API scanly_status_t scanly_cancel_token_create(scanly_cancel_token_t **out_token); +SCANLY_CORE_API void scanly_cancel_token_cancel(scanly_cancel_token_t *token); +SCANLY_CORE_API void scanly_cancel_token_destroy(scanly_cancel_token_t *token); + +SCANLY_CORE_API scanly_status_t scanly_decode( + scanly_context_t *context, + const scanly_image_view_t *image, + const scanly_decode_options_t *options, + scanly_result_set_t **out_results); + +/* + * On DECODE_FAILED or ENGINE_EXECUTION_FAILED, out_results may hold an empty + * result-set containing engine diagnostics. The caller must destroy every + * non-NULL result-set regardless of the returned status. + */ + +SCANLY_CORE_API scanly_status_t scanly_decode_rgba( + scanly_context_t *context, + const uint8_t *data, + size_t data_len, + uint32_t width, + uint32_t height, + size_t row_stride, + const scanly_decode_options_t *options, + scanly_result_set_t **out_results); + +SCANLY_CORE_API scanly_status_t scanly_decode_y_plane( + scanly_context_t *context, + const uint8_t *data, + size_t data_len, + uint32_t width, + uint32_t height, + size_t row_stride, + size_t pixel_stride, + const scanly_decode_options_t *options, + scanly_result_set_t **out_results); + +SCANLY_CORE_API size_t scanly_result_set_count(const scanly_result_set_t *results); +SCANLY_CORE_API scanly_status_t scanly_result_get( + const scanly_result_set_t *results, + size_t index, + scanly_result_view_t *out_view); +SCANLY_CORE_API scanly_status_t scanly_result_set_engine_diagnostics( + const scanly_result_set_t *results, + scanly_engine_diagnostic_view_t *out_view); +SCANLY_CORE_API const char *scanly_result_set_diagnostics_json( + const scanly_result_set_t *results, + size_t *out_len); +SCANLY_CORE_API void scanly_result_set_destroy(scanly_result_set_t *results); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SCANLY_NATIVE_CORE_H */ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-benchmark-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-benchmark-2.1.0.tgz new file mode 100644 index 0000000..40cdd52 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-benchmark-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-browser-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-browser-2.1.0.tgz new file mode 100644 index 0000000..43ce7d6 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-browser-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-core-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-core-2.1.0.tgz new file mode 100644 index 0000000..a7456de Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-core-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-engine-jsqr-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-engine-jsqr-2.1.0.tgz new file mode 100644 index 0000000..f18a87d Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-engine-jsqr-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz new file mode 100644 index 0000000..1b6c757 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz new file mode 100644 index 0000000..18e116c Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-node-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-node-2.1.0.tgz new file mode 100644 index 0000000..5ae9186 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-node-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-parsers-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-parsers-2.1.0.tgz new file mode 100644 index 0000000..b54ce55 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-parsers-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-react-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-react-2.1.0.tgz new file mode 100644 index 0000000..6dd6fef Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-react-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-scenario-schema-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-scenario-schema-2.1.0.tgz new file mode 100644 index 0000000..113b4c8 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-scenario-schema-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz b/release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz new file mode 100644 index 0000000..d8b0394 Binary files /dev/null and b/release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz differ diff --git a/release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json b/release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json new file mode 100644 index 0000000..bbf8f52 --- /dev/null +++ b/release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json @@ -0,0 +1,60 @@ +{ + "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", + "verificationMaterial": { + "certificate": { + "rawBytes": "MIIHbTCCBvKgAwIBAgIUSSd27lU3Rq2FYQr+KawYXISWyDUwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwOTEyMjIyMDAwWhcNMjYwOTEyMjIzMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9zAi1bUI1ORvKxHmuDc3RToQTquQSKtL+2hhhQM/+7n1Pzt7x52Iw9Bos3UhFi0LuG11BpOwuWNAm3Tc+cNJ76OCBhEwggYNMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUqdPr2lKaYzWpIiyTBoWl4W2E5xQwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wewYDVR0RAQH/BHEwb4ZtaHR0cHM6Ly9naXRodWIuY29tL1lhbmdqdW5qaWUtTGluL1NjYW5seS8uZ2l0aHViL3dvcmtmbG93cy92Mi4xLWFydGlmYWN0LXF1YWxpZmljYXRpb24ueW1sQHJlZnMvcHVsbC81OC9tZXJnZTA5BgorBgEEAYO/MAEBBCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMBoGCisGAQQBg78wAQIEDHB1bGxfcmVxdWVzdDA2BgorBgEEAYO/MAEDBChjZDI2NWVhMTU1ZjQ1MTIxNWFiNWQ1ZmYzOTM5ZmNlNDA2OTFhYjAwMCkGCisGAQQBg78wAQQEG3YyLjEgQXJ0aWZhY3QgUXVhbGlmaWNhdGlvbjAjBgorBgEEAYO/MAEFBBVZYW5nanVuamllLUxpbi9TY2FubHkwIAYKKwYBBAGDvzABBgQScmVmcy9wdWxsLzU4L21lcmdlMDsGCisGAQQBg78wAQgELQwraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTB9BgorBgEEAYO/MAEJBG8MbWh0dHBzOi8vZ2l0aHViLmNvbS9ZYW5nanVuamllLUxpbi9TY2FubHkvLmdpdGh1Yi93b3JrZmxvd3MvdjIuMS1hcnRpZmFjdC1xdWFsaWZpY2F0aW9uLnltbEByZWZzL3B1bGwvNTgvbWVyZ2UwOAYKKwYBBAGDvzABCgQqDChjZDI2NWVhMTU1ZjQ1MTIxNWFiNWQ1ZmYzOTM5ZmNlNDA2OTFhYjAwMB0GCisGAQQBg78wAQsEDwwNZ2l0aHViLWhvc3RlZDA4BgorBgEEAYO/MAEMBCoMKGh0dHBzOi8vZ2l0aHViLmNvbS9ZYW5nanVuamllLUxpbi9TY2FubHkwOAYKKwYBBAGDvzABDQQqDChjZDI2NWVhMTU1ZjQ1MTIxNWFiNWQ1ZmYzOTM5ZmNlNDA2OTFhYjAwMCIGCisGAQQBg78wAQ4EFAwScmVmcy9wdWxsLzU4L21lcmdlMBoGCisGAQQBg78wAQ8EDAwKMTE1NjM0MjY1MzAxBgorBgEEAYO/MAEQBCMMIWh0dHBzOi8vZ2l0aHViLmNvbS9ZYW5nanVuamllLUxpbjAZBgorBgEEAYO/MAERBAsMCTIyNDM3MDg5ODB9BgorBgEEAYO/MAESBG8MbWh0dHBzOi8vZ2l0aHViLmNvbS9ZYW5nanVuamllLUxpbi9TY2FubHkvLmdpdGh1Yi93b3JrZmxvd3MvdjIuMS1hcnRpZmFjdC1xdWFsaWZpY2F0aW9uLnltbEByZWZzL3B1bGwvNTgvbWVyZ2UwOAYKKwYBBAGDvzABEwQqDChjZDI2NWVhMTU1ZjQ1MTIxNWFiNWQ1ZmYzOTM5ZmNlNDA2OTFhYjAwMBwGCisGAQQBg78wARQEDgwMcHVsbF9yZXF1ZXN0MFwGCisGAQQBg78wARUETgxMaHR0cHM6Ly9naXRodWIuY29tL1lhbmdqdW5qaWUtTGluL1NjYW5seS9hY3Rpb25zL3J1bnMvMzQ3MjI0MDU5MzEvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzA3BgorBgEEAYO/MAEYBCkMJ3JlcG86WWFuZ2p1bmppZS1MaW4vU2Nhbmx5OnB1bGxfcmVxdWVzdDCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABoJe0wRoAAAQDAEcwRQIhAOHxn7E0qiNFdXoyAYP4o0j1Du28s0sZJo4TmFToL82+AiAoDdhFqQWZ0Of2dZol7uNsIeWzVJA5hUwUQsK8jH3SnzAKBggqhkjOPQQDAwNpADBmAjEAt0XgD7G4ZCm8F912osVPgqDHPR/2A3ZsVxL5+jNNPtOnfGFqZ1cqvBspDwbkpquTAjEA9FwidtqjQbfjpyOLoQlH17CIu9smPY1wnTeBrYxnjg3kMFdf2s3p8xN+2u1x5w5B" + }, + "tlogEntries": [ + { + "logIndex": "2811722131", + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + }, + "kindVersion": { + "kind": "dsse", + "version": "0.0.1" + }, + "integratedTime": "1789251600", + "inclusionPromise": { + "signedEntryTimestamp": "MEYCIQCS5BKzOGO/+qzomJCmJ8Fc2gNlwQec+0T2CafjHSF05QIhAJqnvBOAngGCG/acJuwsSLiomTEes1l12XNx3FrHCawQ" + }, + "inclusionProof": { + "logIndex": "2689817869", + "rootHash": "mMYn7/6EOOE7B4GtRFOBmfWHgaY2NDRI0T27LZgC9nc=", + "treeSize": "2689817871", + "hashes": [ + "QKWmn0dRvZZMAn/iCMCgNTvKajwC7IhfI0vKUnrH2JA=", + "nZIZ6taHGxvl8GuB4dlMCdgv65J3Jwrb27alrocz0rM=", + "pe16hxDuxVfmKL4LVizunstKDLgeSXrsTsgTlsMvaR4=", + "7lsOY3/NsUBjBDYJgjg2EkWwJA0oXEC9SoqetOzvQ8c=", + "HwRRwSHdIkBmtLyy3P4RmmO3b7gBhQQvzwVzGcSbKlA=", + "A93XiHZprOzPr9bmnRfdCVKdbztmhw25FN7nfbPmZc8=", + "w9YDs6i/fgf91zr8Z6vyip7TKRSvmfUth+fVVw5UxQg=", + "N412sS11ltqMsR44ABE4ltb5Gq2IFs1cwRyEqY4Zfks=", + "BNzMYfbKK9Mv7SStOvJgOH0JylJ8Htjl0hSmmGRy8hU=", + "9CXWzMFISsgl52ghEtV74dDkp5OkOFGc1Z4sV7mGxY4=", + "yNp40W2q2/gyniiN8UJnn6UqMwmip9i4SbefrYNwriY=", + "tiUxTqOijHnyrhoLc8Js2aXXMLz0Sdpe/i8BBxTADhA=", + "xEOIRIu9N2D3tu26N1qOzMGE8SYcMA+As1y7A1aX7cg=", + "qxzHanAzz57SDdmJe0B7bJK72NTIBbwEMGBBKvDOROw=", + "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY=" + ], + "checkpoint": { + "envelope": "rekor.sigstore.dev - 1193050959916656506\n2689817871\nmMYn7/6EOOE7B4GtRFOBmfWHgaY2NDRI0T27LZgC9nc=\n\n— rekor.sigstore.dev wNI9ajBEAiAjvgrB4qC4Ug0mOIk6PA9sQndu/e43ql7BZI16GbwM2AIgF7ekxBR3/YRRKpmdoFBBgLdCMDLZArozjt3QCPicRFc=\n" + } + }, + "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMGZhMjYyZDUxMjY2NGU5MTEyMjliNzQ2MTkxMDVlZmY4M2U5ZGFlNzdmN2JhMTYzNzM1YzZmZjc0ZjAyNWZmMiJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6Ijc5NmM3Zjg4NmIyMDZhZWNkNjA5MGY2MDE1MWRlOThjZjRmZDg4YTNjZWE3N2I4MGRjOWZjYTdjMjhlOWM2NzEifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRREM5a2xWYStXVFg4RE82TkpVVWVWdnFwdTRYUEpmTm5QSkRMRGlCeXZsOVFJZ0RMbDMxRzJyR1ErYmQ5cGVrRk9zbWdqQmo3NzFqN1piVEdmdUJPUDRyMXM9IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VoaVZFTkRRblpMWjBGM1NVSkJaMGxWVTFOa01qZHNWVE5TY1RKR1dWRnlLMHRoZDFsWVNWTlhlVVJWZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwWmQwOVVSWGxOYWtsNVRVUkJkMWRvWTA1TmFsbDNUMVJGZVUxcVNYcE5SRUYzVjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVTVla0ZwTVdKVlNURlBVblpMZUVodGRVUmpNMUpVYjFGVWNYVlJVMHQwVENzeWFHZ0thRkZOTHlzM2JqRlFlblEzZURVeVNYYzVRbTl6TTFWb1Jta3dUSFZITVRGQ2NFOTNkVmRPUVcwelZHTXJZMDVLTnpaUFEwSm9SWGRuWjFsT1RVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVnhaRkJ5Q2pKc1MyRlplbGR3U1dsNVZFSnZWMncwVnpKRk5YaFJkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMlYzV1VSV1VqQlNRVkZJTDBKSVJYZGlORnAwWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREZzYUdKdFpIRmtWelZ4WVZkVmRBcFVSMngxVERGT2FsbFhOWE5sVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGt5VFdrMGVFeFhSbmxrUjJ4dFdWZE9NRXhZUmpGWlYzaHdDbHB0YkdwWldGSndZakkwZFdWWE1YTlJTRXBzV201TmRtTklWbk5pUXpneFQwTTVkRnBZU201YVZFRTFRbWR2Y2tKblJVVkJXVTh2VFVGRlFrSkRkRzhLWkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUblppYmxKc1ltNVJkVmt5T1hSTlFtOUhRMmx6UndwQlVWRkNaemM0ZDBGUlNVVkVTRUl4WWtkNFptTnRWbmhrVjFaNlpFUkJNa0puYjNKQ1owVkZRVmxQTDAxQlJVUkNRMmhxV2tSSk1rNVhWbWhOVkZVeENscHFVVEZOVkVsNFRsZEdhVTVYVVRGYWJWbDZUMVJOTlZwdFRteE9SRUV5VDFSR2FGbHFRWGROUTJ0SFEybHpSMEZSVVVKbk56aDNRVkZSUlVjeldYa0tUR3BGWjFGWVNqQmhWMXBvV1ROUloxVllWbWhpUjJ4dFlWZE9hR1JIYkhaaWFrRnFRbWR2Y2tKblJVVkJXVTh2VFVGRlJrSkNWbHBaVnpWdVlXNVdkUXBoYld4c1RGVjRjR0pwT1ZSWk1rWjFZa2hyZDBsQldVdExkMWxDUWtGSFJIWjZRVUpDWjFGVFkyMVdiV041T1hka1YzaHpUSHBWTkV3eU1XeGpiV1JzQ2sxRWMwZERhWE5IUVZGUlFtYzNPSGRCVVdkRlRGRjNjbUZJVWpCalNFMDJUSGs1TUdJeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5vS1dsaEthbUl5TlRCYVZ6VXdURzFPZG1KVVFqbENaMjl5UW1kRlJVRlpUeTlOUVVWS1FrYzRUV0pYYURCa1NFSjZUMms0ZGxveWJEQmhTRlpwVEcxT2RncGlVemxhV1ZjMWJtRnVWblZoYld4c1RGVjRjR0pwT1ZSWk1rWjFZa2hyZGt4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbVJxU1hWTlV6Rm9DbU51VW5CYWJVWnFaRU14ZUdSWFJuTmhWMXB3V1RKR01HRlhPWFZNYm14MFlrVkNlVnBYV25wTU0wSXhZa2QzZGs1VVozWmlWMVo1V2pKVmQwOUJXVXNLUzNkWlFrSkJSMFIyZWtGQ1EyZFJjVVJEYUdwYVJFa3lUbGRXYUUxVVZURmFhbEV4VFZSSmVFNVhSbWxPVjFFeFdtMVplazlVVFRWYWJVNXNUa1JCTWdwUFZFWm9XV3BCZDAxQ01FZERhWE5IUVZGUlFtYzNPSGRCVVhORlJIZDNUbG95YkRCaFNGWnBURmRvZG1NelVteGFSRUUwUW1kdmNrSm5SVVZCV1U4dkNrMUJSVTFDUTI5TlMwZG9NR1JJUW5wUGFUaDJXakpzTUdGSVZtbE1iVTUyWWxNNVdsbFhOVzVoYmxaMVlXMXNiRXhWZUhCaWFUbFVXVEpHZFdKSWEzY0tUMEZaUzB0M1dVSkNRVWRFZG5wQlFrUlJVWEZFUTJocVdrUkpNazVYVm1oTlZGVXhXbXBSTVUxVVNYaE9WMFpwVGxkUk1WcHRXWHBQVkUwMVdtMU9iQXBPUkVFeVQxUkdhRmxxUVhkTlEwbEhRMmx6UjBGUlVVSm5OemgzUVZFMFJVWkJkMU5qYlZadFkzazVkMlJYZUhOTWVsVTBUREl4YkdOdFpHeE5RbTlIQ2tOcGMwZEJVVkZDWnpjNGQwRlJPRVZFUVhkTFRWUkZNVTVxVFRCTmFsa3hUWHBCZUVKbmIzSkNaMFZGUVZsUEwwMUJSVkZDUTAxTlNWZG9NR1JJUW5vS1QyazRkbG95YkRCaFNGWnBURzFPZG1KVE9WcFpWelZ1WVc1V2RXRnRiR3hNVlhod1ltcEJXa0puYjNKQ1owVkZRVmxQTDAxQlJWSkNRWE5OUTFSSmVRcE9SRTB6VFVSbk5VOUVRamxDWjI5eVFtZEZSVUZaVHk5TlFVVlRRa2M0VFdKWGFEQmtTRUo2VDJrNGRsb3liREJoU0ZacFRHMU9kbUpUT1ZwWlZ6VnVDbUZ1Vm5WaGJXeHNURlY0Y0dKcE9WUlpNa1oxWWtocmRreHRaSEJrUjJneFdXazVNMkl6U25KYWJYaDJaRE5OZG1ScVNYVk5VekZvWTI1U2NGcHRSbW9LWkVNeGVHUlhSbk5oVjFwd1dUSkdNR0ZYT1hWTWJteDBZa1ZDZVZwWFducE1NMEl4WWtkM2RrNVVaM1ppVjFaNVdqSlZkMDlCV1V0TGQxbENRa0ZIUkFwMmVrRkNSWGRSY1VSRGFHcGFSRWt5VGxkV2FFMVVWVEZhYWxFeFRWUkplRTVYUm1sT1YxRXhXbTFaZWs5VVRUVmFiVTVzVGtSQk1rOVVSbWhaYWtGM0NrMUNkMGREYVhOSFFWRlJRbWMzT0hkQlVsRkZSR2QzVFdOSVZuTmlSamw1V2xoR01WcFlUakJOUm5kSFEybHpSMEZSVVVKbk56aDNRVkpWUlZSbmVFMEtZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRERnNhR0p0WkhGa1Z6VnhZVmRWZEZSSGJIVk1NVTVxV1ZjMWMyVlRPV2haTTFKd1lqSTFlZ3BNTTBveFltNU5kazE2VVROTmFra3dUVVJWTlUxNlJYWlpXRkl3V2xjeGQyUklUWFpOVkVGWFFtZHZja0puUlVWQldVOHZUVUZGVjBKQlowMUNia0l4Q2xsdGVIQlpla0V6UW1kdmNrSm5SVVZCV1U4dlRVRkZXVUpEYTAxS00wcHNZMGM0TmxkWFJuVmFNbkF4WW0xd2NGcFRNVTFoVnpSMlZUSk9hR0p0ZURVS1QyNUNNV0pIZUdaamJWWjRaRmRXZW1SRVEwSnBaMWxMUzNkWlFrSkJTRmRsVVVsRlFXZFNPRUpJYjBGbFFVSXlRVTR3T1UxSGNrZDRlRVY1V1hoclpRcElTbXh1VG5kTGFWTnNOalF6YW5sMEx6UmxTMk52UVhaTFpUWlBRVUZCUW05S1pUQjNVbTlCUVVGUlJFRkZZM2RTVVVsb1FVOUllRzQzUlRCeGFVNUdDbVJZYjNsQldWQTBiekJxTVVSMU1qaHpNSE5hU204MFZHMUdWRzlNT0RJclFXbEJiMFJrYUVaeFVWZGFNRTltTW1SYWIydzNkVTV6U1dWWGVsWktRVFVLYUZWM1ZWRnpTemhxU0ROVGJucEJTMEpuWjNGb2EycFBVRkZSUkVGM1RuQkJSRUp0UVdwRlFYUXdXR2RFTjBjMFdrTnRPRVk1TVRKdmMxWlFaM0ZFU0FwUVVpOHlRVE5hYzFaNFREVXJhazVPVUhSUGJtWkhSbkZhTVdOeGRrSnpjRVIzWW10d2NYVlVRV3BGUVRsR2QybGtkSEZxVVdKbWFuQjVUMHh2VVd4SUNqRTNRMGwxT1hOdFVGa3hkMjVVWlVKeVdYaHVhbWN6YTAxR1pHWXljek53T0hoT0t6SjFNWGcxZHpWQ0NpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9XX19" + } + ], + "timestampVerificationData": {} + }, + "dsseEnvelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicGtnOm5wbS8lNDBzY2FubHkvdXJsLXNhZmV0eUAyLjEuMCIsImRpZ2VzdCI6eyJzaGE1MTIiOiIyNjllMmYyZWQ3ZGJiNzE5MjZiMWVhZjMzZTA2ZGY4ZWQxN2NmMWEwYWExNmI3NDEyNTJmYTJjNTlhMzkyOTU4OGQ3NTQ5ZTVhYTY2NTVkMTQyYmZmMWIxOTgwM2RiOGFkNWJmMzk0OTU2MmRmNGQ2NWViYTUzYTg0ZDUzMWM1ZSJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjEiLCJwcmVkaWNhdGUiOnsiYnVpbGREZWZpbml0aW9uIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vc2xzYS1mcmFtZXdvcmsuZ2l0aHViLmlvL2dpdGh1Yi1hY3Rpb25zLWJ1aWxkdHlwZXMvd29ya2Zsb3cvdjEiLCJleHRlcm5hbFBhcmFtZXRlcnMiOnsid29ya2Zsb3ciOnsicmVmIjoicmVmcy9wdWxsLzU4L21lcmdlIiwicmVwb3NpdG9yeSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9ZYW5nanVuamllLUxpbi9TY2FubHkiLCJwYXRoIjoiLmdpdGh1Yi93b3JrZmxvd3MvdjIuMS1hcnRpZmFjdC1xdWFsaWZpY2F0aW9uLnltbCJ9LCJzb3VyY2VfY29tbWl0IjoiNmIwMTdiNTNhNDQwNGY0ZmVhYjc3NmFjMTExYjBlYTlkYWNmMjczNCJ9LCJpbnRlcm5hbFBhcmFtZXRlcnMiOnsiZ2l0aHViIjp7ImV2ZW50X25hbWUiOiJwdWxsX3JlcXVlc3QiLCJyZXBvc2l0b3J5X2lkIjoiMTE1NjM0MjY1MyIsInJlcG9zaXRvcnlfb3duZXJfaWQiOiIyMjQzNzA4OTgifX0sInJlc29sdmVkRGVwZW5kZW5jaWVzIjpbeyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL1lhbmdqdW5qaWUtTGluL1NjYW5seUA2YjAxN2I1M2E0NDA0ZjRmZWFiNzc2YWMxMTFiMGVhOWRhY2YyNzM0IiwiZGlnZXN0Ijp7ImdpdENvbW1pdCI6IjZiMDE3YjUzYTQ0MDRmNGZlYWI3NzZhYzExMWIwZWE5ZGFjZjI3MzQifX0seyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL1lhbmdqdW5qaWUtTGluL1NjYW5seUByZWZzL3B1bGwvNTgvbWVyZ2UiLCJkaWdlc3QiOnsiZ2l0Q29tbWl0IjoiY2QyNjVlYTE1NWY0NTEyMTVhYjVkNWZmMzkzOWZjZTQwNjkxYWIwMCJ9fV19LCJydW5EZXRhaWxzIjp7ImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vYWN0aW9ucy9ydW5uZXIvZ2l0aHViLWhvc3RlZCJ9LCJtZXRhZGF0YSI6eyJpbnZvY2F0aW9uSWQiOiJodHRwczovL2dpdGh1Yi5jb20vWWFuZ2p1bmppZS1MaW4vU2Nhbmx5L2FjdGlvbnMvcnVucy8zNDcyMjQwNTkzMS9hdHRlbXB0cy8xIn19fX0=", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "sig": "MEUCIQDC9klVa+WTX8DO6NJUUeVvqpu4XPJfNnPJDLDiByvl9QIgDLl31G2rGQ+bd9pekFOsmgjBj771j7ZbTGfuBOP4r1s=" + } + ] + } +} diff --git a/release/stable/v2.1.0/checksums.sha256 b/release/stable/v2.1.0/checksums.sha256 new file mode 100644 index 0000000..19143b5 --- /dev/null +++ b/release/stable/v2.1.0/checksums.sha256 @@ -0,0 +1,15 @@ +44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b artifacts/android/scanly-sdk-2.1.0.aar +24d04f5d3a67c55aee95af7b4bf158f359a0c4b4da3f37b7df173269bd9af66f artifacts/ios/Package.swift +82dee124d9b88e8ee2c9ea6727a83a3a9e08fb6b70ca753c404928d70624d07a artifacts/native/scanly-core.h +468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd artifacts/npm/scanly-benchmark-2.1.0.tgz +c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727 artifacts/npm/scanly-browser-2.1.0.tgz +7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416 artifacts/npm/scanly-core-2.1.0.tgz +b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b artifacts/npm/scanly-engine-jsqr-2.1.0.tgz +5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz +420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz +13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573 artifacts/npm/scanly-node-2.1.0.tgz +2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868 artifacts/npm/scanly-parsers-2.1.0.tgz +e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff artifacts/npm/scanly-react-2.1.0.tgz +b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1 artifacts/npm/scanly-scenario-schema-2.1.0.tgz +72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87 artifacts/npm/scanly-url-safety-2.1.0.tgz +d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87 artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json diff --git a/release/stable/v2.1.0/deployment.json b/release/stable/v2.1.0/deployment.json new file mode 100644 index 0000000..c84409b --- /dev/null +++ b/release/stable/v2.1.0/deployment.json @@ -0,0 +1,13 @@ +{ + "schemaVersion": "scanly-stable-deployment-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "status": "GO", + "id": "dpl_ZmfWuc5AHqoSn77tsAWFnyeVmb2Y", + "url": "https://qr-decoder-lpc6qiyx6-yangjunjie-lins-projects.vercel.app", + "productionAlias": "https://qr-decoder-theta.vercel.app", + "gitCommitSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "readyState": "READY", + "target": "production" +} diff --git a/release/stable/v2.1.0/evidence/allowed-signers b/release/stable/v2.1.0/evidence/allowed-signers new file mode 100644 index 0000000..cb7e95f --- /dev/null +++ b/release/stable/v2.1.0/evidence/allowed-signers @@ -0,0 +1 @@ +yangjunjie.lin@student.manchester.ac.uk ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFws+cqTLZsX6L8fmZpsMl0C961ThcUZhKI8Tg0olhqY diff --git a/release/stable/v2.1.0/evidence/api-abi.json b/release/stable/v2.1.0/evidence/api-abi.json new file mode 100644 index 0000000..d3ae717 --- /dev/null +++ b/release/stable/v2.1.0/evidence/api-abi.json @@ -0,0 +1,16 @@ +{ + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "publicApi": { + "path": "api-snapshots/public-api.json", + "sha256": "318c825d51b8ee563e10deae96a529243a435739ed1fd00041afab895490680a" + }, + "nativeApi": { + "path": "api-snapshots/native-api.json", + "sha256": "e457c86c9a1ad959d0abc2df83d3c27091af10d864048375ba8a86ca228be411" + }, + "nativeAbi": { + "path": "api-snapshots/native-abi.json", + "sha256": "f9f57e376234df249f9407262dca70ae296aefeca70615e7379245c10c871566" + } +} diff --git a/release/stable/v2.1.0/evidence/deployment-input.json b/release/stable/v2.1.0/evidence/deployment-input.json new file mode 100644 index 0000000..7944ffd --- /dev/null +++ b/release/stable/v2.1.0/evidence/deployment-input.json @@ -0,0 +1,13 @@ +{ + "id": "dpl_ZmfWuc5AHqoSn77tsAWFnyeVmb2Y", + "url": "qr-decoder-lpc6qiyx6-yangjunjie-lins-projects.vercel.app", + "readyState": "READY", + "target": "production", + "createdAt": 1789253952430, + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "alias": [ + "qr-decoder-theta.vercel.app", + "qr-decoder-yangjunjie-lins-projects.vercel.app", + "qr-decoder-git-develop-yangjunjie-lins-projects.vercel.app" + ] +} diff --git a/release/stable/v2.1.0/evidence/native-build-a.json b/release/stable/v2.1.0/evidence/native-build-a.json new file mode 100644 index 0000000..2b159f1 --- /dev/null +++ b/release/stable/v2.1.0/evidence/native-build-a.json @@ -0,0 +1,565 @@ +{ + "conclusion": "success", + "databaseId": 34485084420, + "event": "push", + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "jobs": [ + { + "completedAt": "2026-09-10T13:55:28Z", + "conclusion": "success", + "databaseId": 102897211833, + "name": "iOS SDK", + "startedAt": "2026-09-10T13:49:49Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:49:50Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:49:49Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:55Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-10T13:49:50Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:33Z", + "conclusion": "success", + "name": "Resolve pinned Swift package", + "number": 3, + "startedAt": "2026-09-10T13:49:55Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:51:51Z", + "conclusion": "success", + "name": "Build and test Swift SDK with shared fixtures", + "number": 4, + "startedAt": "2026-09-10T13:50:33Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:54:09Z", + "conclusion": "success", + "name": "Compile generic iOS Simulator SDK", + "number": 5, + "startedAt": "2026-09-10T13:51:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:55:20Z", + "conclusion": "success", + "name": "Compile generic iOS device SDK without signing", + "number": 6, + "startedAt": "2026-09-10T13:54:09Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:55:22Z", + "conclusion": "success", + "name": "Validate Swift module, headers, symbols, and size", + "number": 7, + "startedAt": "2026-09-10T13:55:20Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:55:23Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 8, + "startedAt": "2026-09-10T13:55:22Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:55:24Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 16, + "startedAt": "2026-09-10T13:55:23Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:55:26Z", + "conclusion": "success", + "name": "Complete job", + "number": 17, + "startedAt": "2026-09-10T13:55:24Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102897211833" + }, + { + "completedAt": "2026-09-10T13:50:53Z", + "conclusion": "success", + "databaseId": 102897212052, + "name": "Native Core", + "startedAt": "2026-09-10T13:49:44Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:49:46Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:49:45Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:48Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-10T13:49:46Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:53Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 3, + "startedAt": "2026-09-10T13:49:48Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:07Z", + "conclusion": "success", + "name": "Run npm ci", + "number": 4, + "startedAt": "2026-09-10T13:49:53Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:08Z", + "conclusion": "success", + "name": "Verify generated fixtures and Native contracts", + "number": 5, + "startedAt": "2026-09-10T13:50:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:09Z", + "conclusion": "success", + "name": "Check out pinned ZXing-C++ source", + "number": 6, + "startedAt": "2026-09-10T13:50:08Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:46Z", + "conclusion": "success", + "name": "Build shared Native Core and pinned decoder", + "number": 7, + "startedAt": "2026-09-10T13:50:09Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:47Z", + "conclusion": "success", + "name": "Run C ABI, malformed input, fixture, and multi-result tests", + "number": 8, + "startedAt": "2026-09-10T13:50:46Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:47Z", + "conclusion": "success", + "name": "Verify exported symbol boundary", + "number": 9, + "startedAt": "2026-09-10T13:50:47Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:48Z", + "conclusion": "success", + "name": "Record development benchmark", + "number": 10, + "startedAt": "2026-09-10T13:50:47Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:49Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 11, + "startedAt": "2026-09-10T13:50:48Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:50Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 21, + "startedAt": "2026-09-10T13:50:49Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:51Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 22, + "startedAt": "2026-09-10T13:50:50Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:51Z", + "conclusion": "success", + "name": "Complete job", + "number": 23, + "startedAt": "2026-09-10T13:50:51Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102897212052" + }, + { + "completedAt": "2026-09-10T13:50:09Z", + "conclusion": "success", + "databaseId": 102897212063, + "name": "Native Memory", + "startedAt": "2026-09-10T13:49:44Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:49:45Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:49:44Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:46Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-10T13:49:45Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:06Z", + "conclusion": "success", + "name": "Build ASan/UBSan 10,000-lifecycle tests", + "number": 3, + "startedAt": "2026-09-10T13:49:46Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:07Z", + "conclusion": "success", + "name": "Run memory, ownership, malformed, and C ABI tests", + "number": 4, + "startedAt": "2026-09-10T13:50:06Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:08Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 8, + "startedAt": "2026-09-10T13:50:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:08Z", + "conclusion": "success", + "name": "Complete job", + "number": 9, + "startedAt": "2026-09-10T13:50:08Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102897212063" + }, + { + "completedAt": "2026-09-10T13:56:28Z", + "conclusion": "success", + "databaseId": 102897212254, + "name": "Android SDK", + "startedAt": "2026-09-10T13:49:44Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:49:49Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:49:44Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:51Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-10T13:49:49Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:49:51Z", + "conclusion": "success", + "name": "Run actions/setup-java@v5", + "number": 3, + "startedAt": "2026-09-10T13:49:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:07Z", + "conclusion": "success", + "name": "Run android-actions/setup-android@v4", + "number": 4, + "startedAt": "2026-09-10T13:49:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:07Z", + "conclusion": "success", + "name": "Run gradle/actions/wrapper-validation@v4", + "number": 5, + "startedAt": "2026-09-10T13:50:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:50:28Z", + "conclusion": "success", + "name": "Install Android SDK/NDK toolchain", + "number": 6, + "startedAt": "2026-09-10T13:50:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:52:56Z", + "conclusion": "success", + "name": "Assemble AAR, unit tests, and lint", + "number": 7, + "startedAt": "2026-09-10T13:50:28Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:52:56Z", + "conclusion": "success", + "name": "Enable KVM acceleration for the Android emulator", + "number": 8, + "startedAt": "2026-09-10T13:52:56Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:23Z", + "conclusion": "success", + "name": "Run x86_64 emulator shared-fixture smoke", + "number": 9, + "startedAt": "2026-09-10T13:52:56Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:24Z", + "conclusion": "success", + "name": "Expand and validate AAR", + "number": 10, + "startedAt": "2026-09-10T13:56:23Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:25Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 11, + "startedAt": "2026-09-10T13:56:24Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:26Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 12, + "startedAt": "2026-09-10T13:56:25Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:26Z", + "conclusion": "success", + "name": "Post Run actions/setup-java@v5", + "number": 23, + "startedAt": "2026-09-10T13:56:26Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:26Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 24, + "startedAt": "2026-09-10T13:56:26Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:26Z", + "conclusion": "success", + "name": "Complete job", + "number": 25, + "startedAt": "2026-09-10T13:56:26Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102897212254" + }, + { + "completedAt": "2026-09-10T13:56:58Z", + "conclusion": "success", + "databaseId": 102899639072, + "name": "Native Fixture Parity", + "startedAt": "2026-09-10T13:56:31Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:56:32Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:56:32Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:34Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-10T13:56:32Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:40Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 3, + "startedAt": "2026-09-10T13:56:34Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:51Z", + "conclusion": "success", + "name": "Run npm ci", + "number": 4, + "startedAt": "2026-09-10T13:56:40Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:51Z", + "conclusion": "success", + "name": "Run npm run native:contracts", + "number": 5, + "startedAt": "2026-09-10T13:56:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:56Z", + "conclusion": "success", + "name": "Web/Node shared-engine regression parity", + "number": 6, + "startedAt": "2026-09-10T13:56:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:56Z", + "conclusion": "success", + "name": "Record parity boundary", + "number": 7, + "startedAt": "2026-09-10T13:56:56Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:57Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 8, + "startedAt": "2026-09-10T13:56:56Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:57Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 15, + "startedAt": "2026-09-10T13:56:57Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:57Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 16, + "startedAt": "2026-09-10T13:56:57Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:56:57Z", + "conclusion": "success", + "name": "Complete job", + "number": 17, + "startedAt": "2026-09-10T13:56:57Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102899639072" + }, + { + "completedAt": "2026-09-10T13:57:08Z", + "conclusion": "success", + "databaseId": 102899818420, + "name": "Native Artifact Validation", + "startedAt": "2026-09-10T13:57:01Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-10T13:57:03Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-10T13:57:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:57:04Z", + "conclusion": "success", + "name": "Run actions/download-artifact@v4", + "number": 2, + "startedAt": "2026-09-10T13:57:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:57:06Z", + "conclusion": "success", + "name": "Run actions/download-artifact@v4", + "number": 3, + "startedAt": "2026-09-10T13:57:04Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:57:06Z", + "conclusion": "success", + "name": "Verify aggregated artifacts and truthful boundary", + "number": 4, + "startedAt": "2026-09-10T13:57:06Z", + "status": "completed" + }, + { + "completedAt": "2026-09-10T13:57:06Z", + "conclusion": "success", + "name": "Complete job", + "number": 5, + "startedAt": "2026-09-10T13:57:06Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420/job/102899818420" + } + ], + "status": "completed", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420", + "workflowName": "Native Mobile" +} diff --git a/release/stable/v2.1.0/evidence/native-build-b.json b/release/stable/v2.1.0/evidence/native-build-b.json new file mode 100644 index 0000000..67786ad --- /dev/null +++ b/release/stable/v2.1.0/evidence/native-build-b.json @@ -0,0 +1,565 @@ +{ + "conclusion": "success", + "databaseId": 34721927940, + "event": "workflow_dispatch", + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "jobs": [ + { + "completedAt": "2026-09-12T22:09:11Z", + "conclusion": "success", + "databaseId": 103629299403, + "name": "Native Memory", + "startedAt": "2026-09-12T22:08:56Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:08:58Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:08:57Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:00Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:08:58Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:07Z", + "conclusion": "success", + "name": "Build ASan/UBSan 10,000-lifecycle tests", + "number": 3, + "startedAt": "2026-09-12T22:09:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:08Z", + "conclusion": "success", + "name": "Run memory, ownership, malformed, and C ABI tests", + "number": 4, + "startedAt": "2026-09-12T22:09:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:08Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 8, + "startedAt": "2026-09-12T22:09:08Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:08Z", + "conclusion": "success", + "name": "Complete job", + "number": 9, + "startedAt": "2026-09-12T22:09:08Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103629299403" + }, + { + "completedAt": "2026-09-12T22:16:03Z", + "conclusion": "success", + "databaseId": 103629299485, + "name": "Android SDK", + "startedAt": "2026-09-12T22:08:56Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:08:58Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:08:56Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:00Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:08:58Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:00Z", + "conclusion": "success", + "name": "Run actions/setup-java@v5", + "number": 3, + "startedAt": "2026-09-12T22:09:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:19Z", + "conclusion": "success", + "name": "Run android-actions/setup-android@v4", + "number": 4, + "startedAt": "2026-09-12T22:09:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:19Z", + "conclusion": "success", + "name": "Run gradle/actions/wrapper-validation@v4", + "number": 5, + "startedAt": "2026-09-12T22:09:19Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:37Z", + "conclusion": "success", + "name": "Install Android SDK/NDK toolchain", + "number": 6, + "startedAt": "2026-09-12T22:09:19Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:12:17Z", + "conclusion": "success", + "name": "Assemble AAR, unit tests, and lint", + "number": 7, + "startedAt": "2026-09-12T22:09:37Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:12:17Z", + "conclusion": "success", + "name": "Enable KVM acceleration for the Android emulator", + "number": 8, + "startedAt": "2026-09-12T22:12:17Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:00Z", + "conclusion": "success", + "name": "Run x86_64 emulator shared-fixture smoke", + "number": 9, + "startedAt": "2026-09-12T22:12:17Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:00Z", + "conclusion": "success", + "name": "Expand and validate AAR", + "number": 10, + "startedAt": "2026-09-12T22:16:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:01Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 11, + "startedAt": "2026-09-12T22:16:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:02Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 12, + "startedAt": "2026-09-12T22:16:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:02Z", + "conclusion": "success", + "name": "Post Run actions/setup-java@v5", + "number": 23, + "startedAt": "2026-09-12T22:16:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:02Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 24, + "startedAt": "2026-09-12T22:16:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:16:02Z", + "conclusion": "success", + "name": "Complete job", + "number": 25, + "startedAt": "2026-09-12T22:16:02Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103629299485" + }, + { + "completedAt": "2026-09-12T22:09:55Z", + "conclusion": "success", + "databaseId": 103629299487, + "name": "Native Core", + "startedAt": "2026-09-12T22:08:57Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:08:59Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:08:58Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:01Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:08:59Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:09Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 3, + "startedAt": "2026-09-12T22:09:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:19Z", + "conclusion": "success", + "name": "Run npm ci", + "number": 4, + "startedAt": "2026-09-12T22:09:09Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:20Z", + "conclusion": "success", + "name": "Verify generated fixtures and Native contracts", + "number": 5, + "startedAt": "2026-09-12T22:09:19Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:22Z", + "conclusion": "success", + "name": "Check out pinned ZXing-C++ source", + "number": 6, + "startedAt": "2026-09-12T22:09:20Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:48Z", + "conclusion": "success", + "name": "Build shared Native Core and pinned decoder", + "number": 7, + "startedAt": "2026-09-12T22:09:22Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:49Z", + "conclusion": "success", + "name": "Run C ABI, malformed input, fixture, and multi-result tests", + "number": 8, + "startedAt": "2026-09-12T22:09:48Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:50Z", + "conclusion": "success", + "name": "Verify exported symbol boundary", + "number": 9, + "startedAt": "2026-09-12T22:09:49Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:51Z", + "conclusion": "success", + "name": "Record development benchmark", + "number": 10, + "startedAt": "2026-09-12T22:09:50Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:52Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 11, + "startedAt": "2026-09-12T22:09:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:52Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 21, + "startedAt": "2026-09-12T22:09:52Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:52Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 22, + "startedAt": "2026-09-12T22:09:52Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:52Z", + "conclusion": "success", + "name": "Complete job", + "number": 23, + "startedAt": "2026-09-12T22:09:52Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103629299487" + }, + { + "completedAt": "2026-09-12T22:13:11Z", + "conclusion": "success", + "databaseId": 103629299604, + "name": "iOS SDK", + "startedAt": "2026-09-12T22:09:01Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:09:02Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:09:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:07Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:09:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:09:31Z", + "conclusion": "success", + "name": "Resolve pinned Swift package", + "number": 3, + "startedAt": "2026-09-12T22:09:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:10:34Z", + "conclusion": "success", + "name": "Build and test Swift SDK with shared fixtures", + "number": 4, + "startedAt": "2026-09-12T22:09:31Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:12:16Z", + "conclusion": "success", + "name": "Compile generic iOS Simulator SDK", + "number": 5, + "startedAt": "2026-09-12T22:10:34Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:13:02Z", + "conclusion": "success", + "name": "Compile generic iOS device SDK without signing", + "number": 6, + "startedAt": "2026-09-12T22:12:16Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:13:03Z", + "conclusion": "success", + "name": "Validate Swift module, headers, symbols, and size", + "number": 7, + "startedAt": "2026-09-12T22:13:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:13:05Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 8, + "startedAt": "2026-09-12T22:13:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:13:06Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 16, + "startedAt": "2026-09-12T22:13:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:13:08Z", + "conclusion": "success", + "name": "Complete job", + "number": 17, + "startedAt": "2026-09-12T22:13:06Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103629299604" + }, + { + "completedAt": "2026-09-12T22:19:30Z", + "conclusion": "success", + "databaseId": 103630215309, + "name": "Native Fixture Parity", + "startedAt": "2026-09-12T22:19:00Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:19:02Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:19:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:04Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:19:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:13Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 3, + "startedAt": "2026-09-12T22:19:04Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:22Z", + "conclusion": "success", + "name": "Run npm ci", + "number": 4, + "startedAt": "2026-09-12T22:19:13Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:22Z", + "conclusion": "success", + "name": "Run npm run native:contracts", + "number": 5, + "startedAt": "2026-09-12T22:19:22Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:26Z", + "conclusion": "success", + "name": "Web/Node shared-engine regression parity", + "number": 6, + "startedAt": "2026-09-12T22:19:22Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:26Z", + "conclusion": "success", + "name": "Record parity boundary", + "number": 7, + "startedAt": "2026-09-12T22:19:26Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:27Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 8, + "startedAt": "2026-09-12T22:19:26Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:27Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 15, + "startedAt": "2026-09-12T22:19:27Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:27Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 16, + "startedAt": "2026-09-12T22:19:27Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:27Z", + "conclusion": "success", + "name": "Complete job", + "number": 17, + "startedAt": "2026-09-12T22:19:27Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103630215309" + }, + { + "completedAt": "2026-09-12T22:19:40Z", + "conclusion": "success", + "databaseId": 103630631128, + "name": "Native Artifact Validation", + "startedAt": "2026-09-12T22:19:32Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:19:34Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:19:33Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:36Z", + "conclusion": "success", + "name": "Run actions/download-artifact@v4", + "number": 2, + "startedAt": "2026-09-12T22:19:34Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:37Z", + "conclusion": "success", + "name": "Run actions/download-artifact@v4", + "number": 3, + "startedAt": "2026-09-12T22:19:36Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:37Z", + "conclusion": "success", + "name": "Verify aggregated artifacts and truthful boundary", + "number": 4, + "startedAt": "2026-09-12T22:19:37Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:37Z", + "conclusion": "success", + "name": "Complete job", + "number": 5, + "startedAt": "2026-09-12T22:19:37Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940/job/103630631128" + } + ], + "status": "completed", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940", + "workflowName": "Native Mobile" +} diff --git a/release/stable/v2.1.0/evidence/npm-build-run.json b/release/stable/v2.1.0/evidence/npm-build-run.json new file mode 100644 index 0000000..1cae20d --- /dev/null +++ b/release/stable/v2.1.0/evidence/npm-build-run.json @@ -0,0 +1,240 @@ +{ + "conclusion": "success", + "databaseId": 34722405931, + "event": "pull_request", + "headSha": "1d1eb25f6869103a81e1d828b03b2fe296be79d1", + "jobs": [ + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "databaseId": 103630562477, + "name": "Clean npm build B", + "startedAt": "2026-09-12T22:19:00Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:19:02Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:19:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:05Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:19:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:07Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 3, + "startedAt": "2026-09-12T22:19:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:10Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 4, + "startedAt": "2026-09-12T22:19:07Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:16Z", + "conclusion": "success", + "name": "Install pinned npm", + "number": 5, + "startedAt": "2026-09-12T22:19:10Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:27Z", + "conclusion": "success", + "name": "Verify source identity and install without dependency cache", + "number": 6, + "startedAt": "2026-09-12T22:19:16Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:51Z", + "conclusion": "success", + "name": "Build and verify public packages", + "number": 7, + "startedAt": "2026-09-12T22:19:27Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:02Z", + "conclusion": "success", + "name": "Pack exact artifacts and sign detached provenance (no publication)", + "number": 8, + "startedAt": "2026-09-12T22:19:51Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:03Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 9, + "startedAt": "2026-09-12T22:20:02Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:03Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 16, + "startedAt": "2026-09-12T22:20:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:03Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 17, + "startedAt": "2026-09-12T22:20:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:03Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 18, + "startedAt": "2026-09-12T22:20:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:03Z", + "conclusion": "success", + "name": "Complete job", + "number": 19, + "startedAt": "2026-09-12T22:20:03Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562477" + }, + { + "completedAt": "2026-09-12T22:20:07Z", + "conclusion": "success", + "databaseId": 103630562596, + "name": "Clean npm build A", + "startedAt": "2026-09-12T22:19:00Z", + "status": "completed", + "steps": [ + { + "completedAt": "2026-09-12T22:19:01Z", + "conclusion": "success", + "name": "Set up job", + "number": 1, + "startedAt": "2026-09-12T22:19:00Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:03Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 2, + "startedAt": "2026-09-12T22:19:01Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:05Z", + "conclusion": "success", + "name": "Run actions/checkout@v4", + "number": 3, + "startedAt": "2026-09-12T22:19:03Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:06Z", + "conclusion": "success", + "name": "Run actions/setup-node@v7", + "number": 4, + "startedAt": "2026-09-12T22:19:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:10Z", + "conclusion": "success", + "name": "Install pinned npm", + "number": 5, + "startedAt": "2026-09-12T22:19:06Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:21Z", + "conclusion": "success", + "name": "Verify source identity and install without dependency cache", + "number": 6, + "startedAt": "2026-09-12T22:19:10Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:19:52Z", + "conclusion": "success", + "name": "Build and verify public packages", + "number": 7, + "startedAt": "2026-09-12T22:19:21Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:04Z", + "conclusion": "success", + "name": "Pack exact artifacts and sign detached provenance (no publication)", + "number": 8, + "startedAt": "2026-09-12T22:19:52Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "name": "Run actions/upload-artifact@v4", + "number": 9, + "startedAt": "2026-09-12T22:20:04Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "name": "Post Run actions/setup-node@v7", + "number": 16, + "startedAt": "2026-09-12T22:20:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 17, + "startedAt": "2026-09-12T22:20:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "name": "Post Run actions/checkout@v4", + "number": 18, + "startedAt": "2026-09-12T22:20:05Z", + "status": "completed" + }, + { + "completedAt": "2026-09-12T22:20:05Z", + "conclusion": "success", + "name": "Complete job", + "number": 19, + "startedAt": "2026-09-12T22:20:05Z", + "status": "completed" + } + ], + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562596" + } + ], + "status": "completed", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931" +} diff --git a/release/stable/v2.1.0/evidence/npm-oidc-qualification.json b/release/stable/v2.1.0/evidence/npm-oidc-qualification.json new file mode 100644 index 0000000..dfb21d2 --- /dev/null +++ b/release/stable/v2.1.0/evidence/npm-oidc-qualification.json @@ -0,0 +1,84 @@ +{ + "schemaVersion": "scanly-npm-oidc-qualification-1", + "checkedAt": "2026-09-12T22:49:41.454Z", + "repository": "Yangjunjie-Lin/Scanly", + "workflowFile": "stable-npm-publish.yml", + "workflowRef": "Yangjunjie-Lin/Scanly/.github/workflows/stable-npm-publish.yml@refs/heads/codex/v2.1-release-qualification", + "workflowDefinitionCommit": "29b93b8cc2f17d2b8c03dda0e776176178acc7f6", + "runId": 34723767114, + "runAttempt": 1, + "storedNpmCredentialsUsed": false, + "credentialsRecorded": false, + "configurationModified": false, + "packagesPublished": [], + "results": [ + { + "package": "@scanly/parsers", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/scenario-schema", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/benchmark", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/core", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/engine-jsqr", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/engine-zxing-js", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/engine-zxing-cpp-wasm", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/browser", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/node", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/react", + "status": "OIDC_TOKEN_EXCHANGE_ACCEPTED", + "httpStatus": 201, + "credentialRecorded": false + }, + { + "package": "@scanly/url-safety", + "status": "FIRST_PUBLICATION_BOOTSTRAP_REQUIRED", + "httpStatus": 404, + "credentialRecorded": false + } + ], + "existingTenAccepted": true, + "limitation": "Exchange acceptance proves the matching existing trusted identity can obtain a short-lived npm credential. Final package publication and provenance still require separate verification." +} diff --git a/release/stable/v2.1.0/evidence/npm-reproducibility.json b/release/stable/v2.1.0/evidence/npm-reproducibility.json new file mode 100644 index 0000000..5d0f582 --- /dev/null +++ b/release/stable/v2.1.0/evidence/npm-reproducibility.json @@ -0,0 +1,144 @@ +{ + "schemaVersion": "scanly-v2.1-npm-reproducibility-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "status": "PASS", + "independentBuildJobs": [ + 103630562596, + 103630562477 + ], + "runId": 34722405931, + "cleanBuildRunA": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562596", + "cleanBuildRunB": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562477", + "cleanBuildA": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "cleanBuildB": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "rawBuildAEqualsBuildB": true, + "detachedProvenanceVerified": [ + { + "package": "@scanly/benchmark", + "file": "release-dist/v2.1-build-A/provenance/scanly-benchmark-2.1.0.tgz.sigstore.json", + "sha256": "91f049bf254d70c161350aa7fba48975ad0e1ab0de3dc527067a8ef63052c4f5", + "artifactSha256": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "artifactSha512": "6e262c1f70df8cc03d30f8d096d7cf3fcfbaf392d5e9e7fea4ac3da4d3d098f95c9f064b6a75c738b49e984b4176b1e2b7a0d648e877bc6261962bcbec60d5cf", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/browser", + "file": "release-dist/v2.1-build-A/provenance/scanly-browser-2.1.0.tgz.sigstore.json", + "sha256": "1885ec28511f445404cdce916ae938c8a29e9bd8a00f2c3bacfced7c8eed4689", + "artifactSha256": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "artifactSha512": "07ffa207aa2a82609b0e808ae9221c3c773590406b3fe11dd45e4cf9840fc162170e42efda8d82ef72b39b9f33308e84db818a0239e563b37c66d008c768cf3e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/core", + "file": "release-dist/v2.1-build-A/provenance/scanly-core-2.1.0.tgz.sigstore.json", + "sha256": "f5ceaf7befb913824bd641c5b83955aee7904a428eee7e21e0f893c0fd121448", + "artifactSha256": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "artifactSha512": "c6a81231287f8c9b7a7e30481f65d497496771b003d7d3114a0bc7cdec03baac801baa6c095978b91d77f48ec489a03205508289cdfcdee9b72a5e8ead10e2bd", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-jsqr", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-jsqr-2.1.0.tgz.sigstore.json", + "sha256": "17195d688e9a98a74ec6de80f4c45bc0825676abdae75b3bf8e265f95d84ece6", + "artifactSha256": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "artifactSha512": "4550a7b56196e3f686de41cf278f9f28c9a9d57ccb6e6277a0ff707ecd8422c24d0ba53f63851d181b4b08b62026fc5d2b3191c522bc9c38d7745a0d58854d35", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-zxing-cpp-wasm", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-zxing-cpp-wasm-2.1.0.tgz.sigstore.json", + "sha256": "56ee3f1bc0c0457e1285f6603ce18f1742d35e21a86dbff29ae8055e16fd777e", + "artifactSha256": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "artifactSha512": "46734c49cc0776b26edc1fa6261f78a028a6dd0ec97a7433cd22101648836faeba04c547e90510372fa7da4133d0303f85118cec2b17100581a160cb047bbc39", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-zxing-js", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-zxing-js-2.1.0.tgz.sigstore.json", + "sha256": "3759cf981d334bb26febb30219176162035e109e4c4d7f5fbaf84dcd59c9412d", + "artifactSha256": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "artifactSha512": "49a7c63ddf6317b9662f1be45d92b8ea9289fbb51d4d364a5c1e3e899e82101afd268d26a8af25cb8a739bd4d213f23ea499d4cb5b00e95fff6dc7cfcf0d0fda", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/node", + "file": "release-dist/v2.1-build-A/provenance/scanly-node-2.1.0.tgz.sigstore.json", + "sha256": "560113f3f22de7ade3073de69b9647b65ebefb963453fd1fec7fabff7e59259d", + "artifactSha256": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "artifactSha512": "88f404c159d400a4a03eff00294ad2076dc17dbc84b253c33fc8b742b975becc35d602e9fa529ff0ca91dc635148a8078d5564880973a63c49a2a7c6d1dc337e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/parsers", + "file": "release-dist/v2.1-build-A/provenance/scanly-parsers-2.1.0.tgz.sigstore.json", + "sha256": "e3d6ed1c243081c2b33e65ae2ec075493cb95b2153c050c3289ea62edd49401d", + "artifactSha256": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "artifactSha512": "1aa6833407e9d26ad02b160f253796e73d9517c5c9aa5d6d64795fccc37591f4d1997f443ff5ba34f0c05fea35c9bb217b23916f4b2418ef04f099a88b594755", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/react", + "file": "release-dist/v2.1-build-A/provenance/scanly-react-2.1.0.tgz.sigstore.json", + "sha256": "bc7af9441081c90a3b99725b3fd60c255869b27f26b6e35ae8a04ce167abfbaf", + "artifactSha256": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "artifactSha512": "8c2b8ee908aa097ce7c132a4fbd3671e1264665ed4cd34a7de3cc52188e800acbd2dd8d255cdb10f786b68638310d8301a11fb6c8c71b7e080bda88741f0a497", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/scenario-schema", + "file": "release-dist/v2.1-build-A/provenance/scanly-scenario-schema-2.1.0.tgz.sigstore.json", + "sha256": "4854cbcfad9121028cfd1f44afef9e20a12ccf9c28cc3c8c709d1a279ca123ab", + "artifactSha256": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "artifactSha512": "973fc3a387a978f8f52e63a82f574cf829c2bdd12bb5b0ad7f5766206e8610269d0e69aae90eb45f560f794a9223f9422010d54bc8a40e82ae32c027468eddbe", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/url-safety", + "file": "release-dist/v2.1-build-A/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "sha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + } + ], + "packageCount": 11, + "published": false +} diff --git a/release/stable/v2.1.0/evidence/publication-credentials-input.json b/release/stable/v2.1.0/evidence/publication-credentials-input.json new file mode 100644 index 0000000..d84ad62 --- /dev/null +++ b/release/stable/v2.1.0/evidence/publication-credentials-input.json @@ -0,0 +1,49 @@ +{ + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "checkedAt": "2026-09-12T23:06:58.810Z", + "evidence": { + "registry": "https://registry.npmjs.org/", + "account": "yangjunjielin", + "organization": "scanly", + "organizationRole": "owner", + "trustedPublisherStatus": "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", + "trustedPublisherPackageCount": 10, + "trustedPublisherPackages": [ + "@scanly/parsers", + "@scanly/scenario-schema", + "@scanly/benchmark", + "@scanly/core", + "@scanly/engine-jsqr", + "@scanly/engine-zxing-js", + "@scanly/engine-zxing-cpp-wasm", + "@scanly/browser", + "@scanly/node", + "@scanly/react" + ], + "repository": "Yangjunjie-Lin/Scanly", + "workflowFile": "stable-npm-publish.yml", + "secretValueRecorded": false, + "provenanceWorkflow": ".github/workflows/stable-npm-publish.yml", + "provenanceMechanism": "GITHUB_ACTIONS_OIDC", + "qualificationRun": 34723767114, + "bootstrap": { + "package": "@scanly/url-safety", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "method": "INTERACTIVE_NPM_PROVENANCE_FILE", + "cliAuthenticatedAccount": "yangjunjielin", + "interactiveTwoFactorRequired": true, + "npmCredentialSharedWithCI": false, + "provenanceVerified": true, + "signedReleaseTagRequired": true, + "placeholderVersionAllowed": false, + "artifactPath": "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", + "provenancePath": "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "provenanceSha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87" + } + }, + "publicationPerformed": false +} diff --git a/release/stable/v2.1.0/evidence/security.json b/release/stable/v2.1.0/evidence/security.json new file mode 100644 index 0000000..115f3e8 --- /dev/null +++ b/release/stable/v2.1.0/evidence/security.json @@ -0,0 +1,73 @@ +{ + "gate": "URL_SAFETY_SECURITY_GATE", + "result": "URL_SAFETY_SECURITY_GO", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "repositoryDirty": false, + "testCount": 148, + "assertions": [ + { + "name": "SSRF matrix", + "status": "PASS" + }, + { + "name": "redirect rebinding", + "status": "PASS" + }, + { + "name": "response size", + "status": "PASS" + }, + { + "name": "timeouts", + "status": "PASS" + }, + { + "name": "no cookies/auth", + "status": "PASS" + }, + { + "name": "no JS execution", + "status": "PASS" + }, + { + "name": "provider malformed output", + "status": "PASS" + }, + { + "name": "prompt injection data isolation", + "status": "PASS" + }, + { + "name": "LLM bound and authority", + "status": "PASS" + }, + { + "name": "rate limiting", + "status": "PASS" + }, + { + "name": "privacy modes and redaction", + "status": "PASS" + }, + { + "name": "single-flight and cancellation", + "status": "PASS" + }, + { + "name": "browser/server export isolation", + "status": "PASS" + }, + { + "name": "tree shaking", + "status": "PASS" + }, + { + "name": "secret scan", + "status": "PASS" + } + ], + "browserBundleBytes": 146082, + "localOnlyTreeShakenBytes": 3307, + "limitation": "Mocked transport/provider tests; not live-provider, external penetration-test, or Native qualification." +} diff --git a/release/stable/v2.1.0/evidence/signing-probe.tag b/release/stable/v2.1.0/evidence/signing-probe.tag new file mode 100644 index 0000000..8b01675 --- /dev/null +++ b/release/stable/v2.1.0/evidence/signing-probe.tag @@ -0,0 +1,12 @@ +object 6b017b53a4404f4feab776ac111b0ea9dacf2734 +type commit +tag codex-v2.1-signing-probe-20260912 +tagger Yangjunjie Lin 1789251300 +0100 + +Scanly 2.1.0 temporary signing qualification; not a release +-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgXCz5ypMtmxfovx+ZmmwyXQL3rV +OFxRmEojxODSiWGpgAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 +AAAAQL3WzizQ/lzdSm1+VVevDj0XESTxfSFN2iaVoxNeh3hD10HrvYVAOXSr8VAjUUHdh+ +vatIoqrBzHSGVUCqM+ygQ= +-----END SSH SIGNATURE----- diff --git a/release/stable/v2.1.0/evidence/signing.json b/release/stable/v2.1.0/evidence/signing.json new file mode 100644 index 0000000..55d13c8 --- /dev/null +++ b/release/stable/v2.1.0/evidence/signing.json @@ -0,0 +1,18 @@ +{ + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "evidence": { + "scheme": "SSH_ED25519", + "githubAccount": "Yangjunjie-Lin", + "githubSigningKeyId": 1118906, + "publicKeyFingerprint": "SHA256:1odyhkHV3hKHFlE8QrELlBGMM/qlM5ftmiiVym/M6ck", + "publicKeyRegisteredAt": "2026-08-18T01:00:29.903+01:00", + "localSmokeTest": { + "status": "PASS", + "annotatedTagObjectCreated": true, + "sshSignatureBlockPresent": true, + "temporaryTagDeleted": true, + "tagObject": "60d17d680d457103be803e2e0f789320b1a00521" + } + } +} diff --git a/release/stable/v2.1.0/evidence/software.json b/release/stable/v2.1.0/evidence/software.json new file mode 100644 index 0000000..d28004c --- /dev/null +++ b/release/stable/v2.1.0/evidence/software.json @@ -0,0 +1,88 @@ +{ + "schemaVersion": "scanly-source-software-qualification-1", + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "runs": [ + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084366, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:59:26Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084366", + "workflowName": "CI" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084384, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:50:49Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084384", + "workflowName": "Public API" + }, + { + "conclusion": "success", + "createdAt": "2026-09-12T22:08:52Z", + "databaseId": 34721927940, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-12T22:19:40Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940", + "workflowName": "Native Mobile" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084437, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T14:09:46Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084437", + "workflowName": "Full Benchmark" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084391, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:52:29Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084391", + "workflowName": "Browser Benchmark" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084436, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:50:15Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084436", + "workflowName": "Tracking Benchmark" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084443, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:51:19Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084443", + "workflowName": "Industrial Benchmark" + }, + { + "conclusion": "success", + "createdAt": "2026-09-10T13:49:40Z", + "databaseId": 34485084429, + "headSha": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "status": "completed", + "updatedAt": "2026-09-10T13:50:42Z", + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084429", + "workflowName": "Device Evidence Validation" + } + ] +} diff --git a/release/stable/v2.1.0/evidence/url-safety-benchmark.json b/release/stable/v2.1.0/evidence/url-safety-benchmark.json new file mode 100644 index 0000000..28437a2 --- /dev/null +++ b/release/stable/v2.1.0/evidence/url-safety-benchmark.json @@ -0,0 +1,26 @@ +{ + "kind": "url-safety-development-benchmark", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "repositoryDirty": false, + "baseline": { + "sdkVersion": "2.0.1", + "medianMs": 21.981802000000016, + "p95Ms": 23.008917999999994 + }, + "current": { + "sdkVersion": "2.1.0", + "medianMs": 21.809138000000075, + "p95Ms": 22.883414000000016 + }, + "disabledMedianDeltaMs": -0.17266399999994064, + "disabledRegressionPass": true, + "disabledEventUs": 0.07603598999999918, + "enabledDispatchMs": 0.8800129999999626, + "localP50Ms": 0.03333200000002989, + "localP95Ms": 0.07566200000002254, + "repeatedUrls": 100, + "remoteCalls": 1, + "latencyIndependentOfNetwork": true, + "limitation": "Same-machine Node fixture baseline, not browser FPS or physical-device qualification; existing browser/realtime benchmarks remain required." +} diff --git a/release/stable/v2.1.0/evidence/url-safety-tests.json b/release/stable/v2.1.0/evidence/url-safety-tests.json new file mode 100644 index 0000000..c783acb --- /dev/null +++ b/release/stable/v2.1.0/evidence/url-safety-tests.json @@ -0,0 +1 @@ +{"numTotalTestSuites":10,"numPassedTestSuites":10,"numFailedTestSuites":0,"numPendingTestSuites":0,"numTotalTests":148,"numPassedTests":148,"numFailedTests":0,"numPendingTests":0,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1789048459766,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership hashes normalized URL, dedupes 100 concurrent and sequential calls within TTL","status":"passed","title":"hashes normalized URL, dedupes 100 concurrent and sequential calls within TTL","duration":21.563440000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership expires TTL and separates mode scopes","status":"passed","title":"expires TTL and separates mode scopes","duration":23.80938100000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership one subscriber cancellation cannot abort another; last subscriber cancels shared work","status":"passed","title":"one subscriber cancellation cannot abort another; last subscriber cancels shared work","duration":13.739399999999932,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership bounds in-flight entries and can recover from failed work","status":"passed","title":"bounds in-flight entries and can recover from failed work","duration":7.077287000000069,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership defaults disabled, ignores raw detection and non-URLs, and starts after result publication","status":"passed","title":"defaults disabled, ignores raw detection and non-URLs, and starts after result publication","duration":15.632421999999906,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership cancels A and suppresses its late result after B","status":"passed","title":"cancels A and suppresses its late result after B","duration":22.834211000000096,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership isolates failures and consumer callback errors from decoding","status":"passed","title":"isolates failures and consumer callback errors from decoding","duration":5.907153999999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["cache and scanner ownership"],"fullName":"cache and scanner ownership attaches to a real ScannerSession without blocking results and drains on stop/dispose","status":"passed","title":"attaches to a real ScannerSession without blocking results and drains on stop/dispose","duration":9.602949999999964,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["browser/backend request boundary"],"fullName":"browser/backend request boundary browser local-only sends zero network; remote sends only URL and mode to same-origin endpoint","status":"passed","title":"browser local-only sends zero network; remote sends only URL and mode to same-origin endpoint","duration":29.70051599999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["browser/backend request boundary"],"fullName":"browser/backend request boundary fails closed on malformed/oversized/error client response","status":"passed","title":"fails closed on malformed/oversized/error client response","duration":13.462462999999843,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["browser/backend request boundary"],"fullName":"browser/backend request boundary enforces origin, method, JSON schema and both declared/streamed request size","status":"passed","title":"enforces origin, method, JSON schema and both declared/streamed request size","duration":13.218508000000156,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["browser/backend request boundary"],"fullName":"browser/backend request boundary rate limits without trusting forged forwarded IP headers","status":"passed","title":"rate limits without trusting forged forwarded IP headers","duration":1.817211000000043,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["browser/backend request boundary"],"fullName":"browser/backend request boundary bounds concurrency/timeout, propagates cancellation and does not expose errors","status":"passed","title":"bounds concurrency/timeout, propagates cancellation and does not expose errors","duration":22.05377599999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1789048461447,"endTime":1789048461648.0537,"status":"passed","message":"","name":"/home/runner/work/Scanly/Scanly/tests/unit/url-safety/lifecycle.test.ts"},{"assertionResults":[{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence uses WHATWG normalization, preserving path/query semantics","status":"passed","title":"uses WHATWG normalization, preserving path/query semantics","duration":1.9928879999999936,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence keeps Unicode and ASCII hostnames","status":"passed","title":"keeps Unicode and ASCII hostnames","duration":1.0572429999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input invalid","status":"passed","title":"rejects unsupported/invalid input invalid","duration":0.8334250000000054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input javascript:alert(1)","status":"passed","title":"rejects unsupported/invalid input javascript:alert(1)","duration":0.23232300000000805,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input data:text/plain,hello","status":"passed","title":"rejects unsupported/invalid input data:text/plain,hello","duration":0.30701299999998355,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input file:///etc/passwd","status":"passed","title":"rejects unsupported/invalid input file:///etc/passwd","duration":0.23145200000001864,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input ftp://example.com","status":"passed","title":"rejects unsupported/invalid input ftp://example.com","duration":0.21727500000000077,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence rejects unsupported/invalid input https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","status":"passed","title":"rejects unsupported/invalid input https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","duration":0.3349749999999858,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for http://example.com","status":"passed","title":"emits a signal for http://example.com","duration":1.178179,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://8.8.8.8","status":"passed","title":"emits a signal for https://8.8.8.8","duration":0.43296799999998825,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://localhost","status":"passed","title":"emits a signal for https://localhost","duration":0.2802130000000034,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://test.internal","status":"passed","title":"emits a signal for https://test.internal","duration":0.3075629999999876,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://192.168.1.2","status":"passed","title":"emits a signal for https://192.168.1.2","duration":0.31415699999999447,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com:8443","status":"passed","title":"emits a signal for https://example.com:8443","duration":0.41504500000002054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://user:pass@example.com","status":"passed","title":"emits a signal for https://user:pass@example.com","duration":0.47271200000000135,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","status":"passed","title":"emits a signal for https://example.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","duration":0.35214600000000473,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://a.b.c.d.e.example.com","status":"passed","title":"emits a signal for https://a.b.c.d.e.example.com","duration":0.3266099999999881,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://xn--bcher-kva.example","status":"passed","title":"emits a signal for https://xn--bcher-kva.example","duration":1.4997590000000116,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://раypal.example","status":"passed","title":"emits a signal for https://раypal.example","duration":0.371042999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://abcdefghijklmnopqrstuvwxy12345.example.com","status":"passed","title":"emits a signal for https://abcdefghijklmnopqrstuvwxy12345.example.com","duration":0.2917539999999974,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://abcde12345xyzpq98765.example.com","status":"passed","title":"emits a signal for https://abcde12345xyzpq98765.example.com","duration":0.18600699999998938,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.zip","status":"passed","title":"emits a signal for https://example.zip","duration":0.16788400000001502,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://bit.ly/x","status":"passed","title":"emits a signal for https://bit.ly/x","duration":0.17945499999999015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://paypal.attacker.com","status":"passed","title":"emits a signal for https://paypal.attacker.com","duration":0.20855899999997973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://paypal-payments.com/login","status":"passed","title":"emits a signal for https://paypal-payments.com/login","duration":0.16479800000001887,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/?next=x&redirect=y","status":"passed","title":"emits a signal for https://example.com/?next=x&redirect=y","duration":0.6164300000000082,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/?next=https%3A%2F%2Fother.com","status":"passed","title":"emits a signal for https://example.com/?next=https%3A%2F%2Fother.com","duration":0.23825400000001196,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/?token=abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd","status":"passed","title":"emits a signal for https://example.com/?token=abcdabcdabcdabcdabcdabcdabcdabcdabcdabcd","duration":0.2747420000000034,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/file.apk","status":"passed","title":"emits a signal for https://example.com/file.apk","duration":0.16332400000001712,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence emits a signal for https://example.com/%6cogin","status":"passed","title":"emits a signal for https://example.com/%6cogin","duration":0.18752000000000635,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not treat lexical signals as proof of maliciousness","status":"passed","title":"does not treat lexical signals as proof of maliciousness","duration":1.0159360000000106,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not mistake a public IPv6 literal for an unqualified private hostname","status":"passed","title":"does not mistake a public IPv6 literal for an unqualified private hostname","duration":0.23783299999999485,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence redacts all query values, userinfo, fragment and free-text secrets","status":"passed","title":"redacts all query values, userinfo, fragment and free-text secrets","duration":1.2475980000000106,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not route non-URL payload plain text","status":"passed","title":"does not route non-URL payload plain text","duration":0.869261999999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not route non-URL payload WIFI:T:WPA;S:home;P:secret;;","status":"passed","title":"does not route non-URL payload WIFI:T:WPA;S:home;P:secret;;","duration":0.22545999999999822,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not route non-URL payload BEGIN:VCARD\nFN:Person\nEND:VCARD","status":"passed","title":"does not route non-URL payload BEGIN:VCARD\nFN:Person\nEND:VCARD","duration":0.24639000000001943,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence does not route non-URL payload mailto:x@example.com","status":"passed","title":"does not route non-URL payload mailto:x@example.com","duration":0.19189799999998058,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["URL canonicalization and local-only evidence"],"fullName":"URL canonicalization and local-only evidence routes existing URL and GS1 parser output only","status":"passed","title":"routes existing URL and GS1 parser output only","duration":0.6433309999999892,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority keeps MALWARE above a benign LLM","status":"passed","title":"keeps MALWARE above a benign LLM","duration":0.24850400000002537,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority keeps SOCIAL_ENGINEERING above a benign LLM","status":"passed","title":"keeps SOCIAL_ENGINEERING above a benign LLM","duration":0.2439059999999813,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority keeps UNWANTED_SOFTWARE above a benign LLM","status":"passed","title":"keeps UNWANTED_SOFTWARE above a benign LLM","duration":0.16498799999999392,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority never upgrades no-match or benign AI to safe","status":"passed","title":"never upgrades no-match or benign AI to safe","duration":0.36282700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority combines local mismatch, credential form and advisory phishing","status":"passed","title":"combines local mismatch, credential form and advisory phishing","duration":0.404925999999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["deterministic evidence authority"],"fullName":"deterministic evidence authority bounds LLM even if invoked directly with invalid scores","status":"passed","title":"bounds LLM even if invoked directly with invalid scores","duration":0.2843110000000024,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1789048462207,"endTime":1789048462227.405,"status":"passed","message":"","name":"/home/runner/work/Scanly/Scanly/tests/unit/url-safety/local.test.ts"},{"assertionResults":[{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary supports zero credentials without failing the analyzer","status":"passed","title":"supports zero credentials without failing the analyzer","duration":18.67154099999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary Google adapter only calls fixed API, supports no-match and high-authority threats","status":"passed","title":"Google adapter only calls fixed API, supports no-match and high-authority threats","duration":27.883623999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects malformed Web Risk output null","status":"passed","title":"rejects malformed Web Risk output null","duration":2.287599,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects malformed Web Risk output { error: 'bad' }","status":"passed","title":"rejects malformed Web Risk output { error: 'bad' }","duration":0.864202999999975,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects malformed Web Risk output { threat: {} }","status":"passed","title":"rejects malformed Web Risk output { threat: {} }","duration":0.8987870000000271,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects malformed Web Risk output { threat: { threatTypes: [ 'SAFE' ] } }","status":"passed","title":"rejects malformed Web Risk output { threat: { threatTypes: [ 'SAFE' ] } }","duration":0.7458010000000286,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary VirusTotal looks up existing reports without submission/rescan","status":"passed","title":"VirusTotal looks up existing reports without submission/rescan","duration":1.9835010000000466,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects malformed VT stats and HTTP provider failures","status":"passed","title":"rejects malformed VT stats and HTTP provider failures","duration":1.639519000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary places multilingual injection only in data, with immutable policy and no tools","status":"passed","title":"places multilingual injection only in data, with immutable policy and no tools","duration":4.300504999999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI not json","status":"passed","title":"fails closed on malformed AI not json","duration":2.4036560000000122,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI ```json {} ```","status":"passed","title":"fails closed on malformed AI ```json {} ```","duration":1.2870319999999538,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {}","status":"passed","title":"fails closed on malformed AI {}","duration":1.1649040000000355,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"safe\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"safe\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","duration":1.2291139999999814,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":21,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":21,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","duration":1.2203069999999911,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":-1,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":-1,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","duration":1.3844239999999672,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":\"20\",\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":\"20\",\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","duration":1.051733000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"],\"tools\":[\"shell\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"],\"tools\":[\"shell\"]}","duration":1.4631100000000288,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"certain\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"certain\",\"riskContribution\":0,\"reasons\":[\"No high-risk indicators in supplied evidence.\"]}","duration":1.205979999999954,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[]}","status":"passed","title":"fails closed on malformed AI {\"classification\":\"benign\",\"confidence\":\"high\",\"riskContribution\":0,\"reasons\":[]}","duration":2.789605999999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary fails closed on malformed AI xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","status":"passed","title":"fails closed on malformed AI xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","duration":0.8981559999999718,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary AI HTTP 429 does not crash the analyzer","status":"passed","title":"AI HTTP 429 does not crash the analyzer","duration":1.2222409999999968,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary AI HTTP 500 does not crash the analyzer","status":"passed","title":"AI HTTP 500 does not crash the analyzer","duration":0.7837129999999775,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary bounds AI timeout, custom provider score, and total budget","status":"passed","title":"bounds AI timeout, custom provider score, and total budget","duration":43.44798700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary rejects oversized HTML; bounds extraction and handles malformed HTML","status":"passed","title":"rejects oversized HTML; bounds extraction and handles malformed HTML","duration":130.10103500000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary keeps privacy modes isolated","status":"passed","title":"keeps privacy modes isolated","duration":3.3448319999999967,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["provider adapters and strict AI data boundary"],"fullName":"provider adapters and strict AI data boundary does not transmit embedded credentials and represents network/AI unavailability","status":"passed","title":"does not transmit embedded credentials and represents network/AI unavailability","duration":1.864659999999958,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1789048460222,"endTime":1789048460478.8647,"status":"passed","message":"","name":"/home/runner/work/Scanly/Scanly/tests/unit/url-safety/providers.test.ts"},{"assertionResults":[{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks Azure public-looking metadata infrastructure","status":"passed","title":"blocks Azure public-looking metadata infrastructure","duration":4.568625999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 127.0.0.1 before any socket","status":"passed","title":"blocks 127.0.0.1 before any socket","duration":1.3436679999999797,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks localhost before any socket","status":"passed","title":"blocks localhost before any socket","duration":3.8868230000000494,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 0.0.0.0 before any socket","status":"passed","title":"blocks 0.0.0.0 before any socket","duration":0.6857399999999529,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 10.0.0.1 before any socket","status":"passed","title":"blocks 10.0.0.1 before any socket","duration":0.7887720000000513,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 172.16.0.1 before any socket","status":"passed","title":"blocks 172.16.0.1 before any socket","duration":0.4347319999999968,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 172.31.255.255 before any socket","status":"passed","title":"blocks 172.31.255.255 before any socket","duration":0.47581800000000385,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 192.168.1.1 before any socket","status":"passed","title":"blocks 192.168.1.1 before any socket","duration":0.35953100000000404,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 169.254.169.254 before any socket","status":"passed","title":"blocks 169.254.169.254 before any socket","duration":0.6774040000000241,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 169.254.1.1 before any socket","status":"passed","title":"blocks 169.254.1.1 before any socket","duration":0.7943520000000035,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 100.64.0.1 before any socket","status":"passed","title":"blocks 100.64.0.1 before any socket","duration":0.5197499999999877,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 224.0.0.1 before any socket","status":"passed","title":"blocks 224.0.0.1 before any socket","duration":0.41705799999999726,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 255.255.255.255 before any socket","status":"passed","title":"blocks 255.255.255.255 before any socket","duration":0.4538069999999834,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 192.0.2.1 before any socket","status":"passed","title":"blocks 192.0.2.1 before any socket","duration":0.5279449999999883,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 198.18.1.1 before any socket","status":"passed","title":"blocks 198.18.1.1 before any socket","duration":0.45883600000001934,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 198.51.100.1 before any socket","status":"passed","title":"blocks 198.51.100.1 before any socket","duration":0.41339199999998755,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 203.0.113.1 before any socket","status":"passed","title":"blocks 203.0.113.1 before any socket","duration":0.31683100000003606,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 240.0.0.1 before any socket","status":"passed","title":"blocks 240.0.0.1 before any socket","duration":0.4040640000000053,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 0x7f000001 before any socket","status":"passed","title":"blocks 0x7f000001 before any socket","duration":0.38428699999997207,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 2130706433 before any socket","status":"passed","title":"blocks 2130706433 before any socket","duration":0.47019699999998466,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 0177.0.0.1 before any socket","status":"passed","title":"blocks 0177.0.0.1 before any socket","duration":0.42962199999999484,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks 127.1 before any socket","status":"passed","title":"blocks 127.1 before any socket","duration":0.3422589999999559,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [::1] before any socket","status":"passed","title":"blocks [::1] before any socket","duration":2.296795999999972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [::] before any socket","status":"passed","title":"blocks [::] before any socket","duration":0.5750830000000065,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [fe80::1] before any socket","status":"passed","title":"blocks [fe80::1] before any socket","duration":0.36702500000001237,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [fc00::1] before any socket","status":"passed","title":"blocks [fc00::1] before any socket","duration":0.44638300000002573,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [fd00::1] before any socket","status":"passed","title":"blocks [fd00::1] before any socket","duration":0.35219699999998966,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [ff02::1] before any socket","status":"passed","title":"blocks [ff02::1] before any socket","duration":0.353779999999972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [::ffff:127.0.0.1] before any socket","status":"passed","title":"blocks [::ffff:127.0.0.1] before any socket","duration":0.5097610000000259,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [::ffff:192.168.0.1] before any socket","status":"passed","title":"blocks [::ffff:192.168.0.1] before any socket","duration":0.4071789999999851,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [::ffff:8.8.8.8] before any socket","status":"passed","title":"blocks [::ffff:8.8.8.8] before any socket","duration":0.3311469999999872,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [64:ff9b::a00:1] before any socket","status":"passed","title":"blocks [64:ff9b::a00:1] before any socket","duration":0.3777949999999919,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [2002:7f00:1::] before any socket","status":"passed","title":"blocks [2002:7f00:1::] before any socket","duration":0.5084190000000035,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [2001:db8::1] before any socket","status":"passed","title":"blocks [2001:db8::1] before any socket","duration":0.4904549999999972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks [3fff::1] before any socket","status":"passed","title":"blocks [3fff::1] before any socket","duration":0.7976390000000038,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks metadata.google.internal before any socket","status":"passed","title":"blocks metadata.google.internal before any socket","duration":0.348811000000012,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL file:///etc/passwd","status":"passed","title":"blocks forbidden URL file:///etc/passwd","duration":0.3135449999999764,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL ftp://example.com","status":"passed","title":"blocks forbidden URL ftp://example.com","duration":0.2041910000000371,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL data:text/plain,hello","status":"passed","title":"blocks forbidden URL data:text/plain,hello","duration":0.1948239999999828,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL javascript:x","status":"passed","title":"blocks forbidden URL javascript:x","duration":0.1845739999999978,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL blob:https://example.com/id","status":"passed","title":"blocks forbidden URL blob:https://example.com/id","duration":0.2337259999999901,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL gopher://example.com","status":"passed","title":"blocks forbidden URL gopher://example.com","duration":0.191237000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL ws://example.com","status":"passed","title":"blocks forbidden URL ws://example.com","duration":0.1839429999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL wss://example.com","status":"passed","title":"blocks forbidden URL wss://example.com","duration":0.18975399999999354,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL https://user:pass@example.com","status":"passed","title":"blocks forbidden URL https://user:pass@example.com","duration":0.31545899999997573,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL https://example.com:8080","status":"passed","title":"blocks forbidden URL https://example.com:8080","duration":0.28949999999997544,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary blocks forbidden URL not a url","status":"passed","title":"blocks forbidden URL not a url","duration":0.3311479999999847,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary validates every A answer 10.0.0.1","status":"passed","title":"validates every A answer 10.0.0.1","duration":0.6582390000000373,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary validates every A answer 93.184.216.34","status":"passed","title":"validates every A answer 93.184.216.34","duration":0.47989599999999655,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary validates AAAA even when A is public","status":"passed","title":"validates AAAA even when A is public","duration":0.5716570000000161,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary revalidates redirect to http://127.0.0.1","status":"passed","title":"revalidates redirect to http://127.0.0.1","duration":2.701412000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary revalidates redirect to http://169.254.169.254","status":"passed","title":"revalidates redirect to http://169.254.169.254","duration":0.6076339999999618,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary revalidates redirect to http://[::1]","status":"passed","title":"revalidates redirect to http://[::1]","duration":0.6663839999999936,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary revalidates redirect to file:///etc/passwd","status":"passed","title":"revalidates redirect to file:///etc/passwd","duration":0.6594800000000305,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary pins connection to checked IP and re-resolves same-host redirects against rebinding","status":"passed","title":"pins connection to checked IP and re-resolves same-host redirects against rebinding","duration":2.1085749999999734,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary permits three redirects, never four","status":"passed","title":"permits three redirects, never four","duration":1.6296709999999734,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary extracts only bounded metadata and does not run scripts/subresources","status":"passed","title":"extracts only bounded metadata and does not run scripts/subresources","duration":8.20818700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary bounds body/encoding { headers: { 'content-type': 'text/html', 'content-length': '2048' } }","status":"passed","title":"bounds body/encoding { headers: { 'content-type': 'text/html', 'content-length': '2048' } }","duration":0.7040230000000065,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary bounds body/encoding { chunks: [ 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy' ] }","status":"passed","title":"bounds body/encoding { chunks: [ 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy' ] }","duration":0.6014320000000453,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary bounds body/encoding { headers: { 'content-type': 'text/html', 'content-encoding': 'gzip' } }","status":"passed","title":"bounds body/encoding { headers: { 'content-type': 'text/html', 'content-encoding': 'gzip' } }","duration":0.4848039999999969,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary only records metadata for downloads and handles text/plain","status":"passed","title":"only records metadata for downloads and handles text/plain","duration":1.0760970000000043,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary redacts query values echoed into page title and visible text","status":"passed","title":"redacts query values echoed into page title and visible text","duration":1.0646370000000047,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary bounds hung DNS/request and hides transport error detail","status":"passed","title":"bounds hung DNS/request and hides transport error detail","duration":21.994024000000024,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary fails closed on DNS errors, empty answers and missing redirect location","status":"passed","title":"fails closed on DNS errors, empty answers and missing redirect location","duration":1.025935000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SSRF hard boundary"],"fullName":"SSRF hard boundary supports caller cancellation and validates configuration","status":"passed","title":"supports caller cancellation and validates configuration","duration":2.0045709999999985,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1789048461965,"endTime":1789048462043.0046,"status":"passed","message":"","name":"/home/runner/work/Scanly/Scanly/tests/unit/url-safety/ssrf.test.ts"}]} \ No newline at end of file diff --git a/release/stable/v2.1.0/license-inventory.json b/release/stable/v2.1.0/license-inventory.json new file mode 100644 index 0000000..220d3e7 --- /dev/null +++ b/release/stable/v2.1.0/license-inventory.json @@ -0,0 +1,3208 @@ +{ + "schemaVersion": "scanly-stable-license-inventory-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "status": "GO", + "unknownLicenseCount": 0, + "components": [ + { + "name": "@axe-core/playwright", + "version": "4.13.0", + "license": "MPL-2.0", + "purl": "pkg:npm/%40axe-core/playwright@4.13.0" + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "purl": "pkg:npm/%40babel/helper-string-parser@7.29.7" + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "purl": "pkg:npm/%40babel/helper-validator-identifier@7.29.7" + }, + { + "name": "@babel/parser", + "version": "7.29.8", + "license": "MIT", + "purl": "pkg:npm/%40babel/parser@7.29.8" + }, + { + "name": "@babel/types", + "version": "7.29.8", + "license": "MIT", + "purl": "pkg:npm/%40babel/types@7.29.8" + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/%40bcoe/v8-coverage@1.0.2" + }, + { + "name": "@emnapi/core", + "version": "1.10.0", + "license": "MIT", + "purl": "pkg:npm/%40emnapi/core@1.10.0" + }, + { + "name": "@emnapi/runtime", + "version": "1.10.0", + "license": "MIT", + "purl": "pkg:npm/%40emnapi/runtime@1.10.0" + }, + { + "name": "@emnapi/runtime", + "version": "1.11.3", + "license": "MIT", + "purl": "pkg:npm/%40emnapi/runtime@1.11.3" + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/%40emnapi/wasi-threads@1.2.1" + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/aix-ppc64@0.28.2" + }, + { + "name": "@esbuild/android-arm", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/android-arm@0.28.2" + }, + { + "name": "@esbuild/android-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/android-arm64@0.28.2" + }, + { + "name": "@esbuild/android-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/android-x64@0.28.2" + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/darwin-arm64@0.28.2" + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/darwin-x64@0.28.2" + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/freebsd-arm64@0.28.2" + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/freebsd-x64@0.28.2" + }, + { + "name": "@esbuild/linux-arm", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-arm@0.28.2" + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-arm64@0.28.2" + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-ia32@0.28.2" + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-loong64@0.28.2" + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-mips64el@0.28.2" + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-ppc64@0.28.2" + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-riscv64@0.28.2" + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-s390x@0.28.2" + }, + { + "name": "@esbuild/linux-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/linux-x64@0.28.2" + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/netbsd-arm64@0.28.2" + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/netbsd-x64@0.28.2" + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/openbsd-arm64@0.28.2" + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/openbsd-x64@0.28.2" + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/openharmony-arm64@0.28.2" + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/sunos-x64@0.28.2" + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/win32-arm64@0.28.2" + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/win32-ia32@0.28.2" + }, + { + "name": "@esbuild/win32-x64", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/%40esbuild/win32-x64@0.28.2" + }, + { + "name": "@eslint-community/eslint-utils", + "version": "4.9.1", + "license": "MIT", + "purl": "pkg:npm/%40eslint-community/eslint-utils@4.9.1" + }, + { + "name": "@eslint-community/regexpp", + "version": "4.12.2", + "license": "MIT", + "purl": "pkg:npm/%40eslint-community/regexpp@4.12.2" + }, + { + "name": "@eslint/eslintrc", + "version": "2.1.4", + "license": "MIT", + "purl": "pkg:npm/%40eslint/eslintrc@2.1.4" + }, + { + "name": "@eslint/js", + "version": "8.57.0", + "license": "MIT", + "purl": "pkg:npm/%40eslint/js@8.57.0" + }, + { + "name": "@humanwhocodes/config-array", + "version": "0.11.14", + "license": "Apache-2.0", + "purl": "pkg:npm/%40humanwhocodes/config-array@0.11.14" + }, + { + "name": "@humanwhocodes/module-importer", + "version": "1.0.1", + "license": "Apache-2.0", + "purl": "pkg:npm/%40humanwhocodes/module-importer@1.0.1" + }, + { + "name": "@humanwhocodes/object-schema", + "version": "2.0.3", + "license": "BSD-3-Clause", + "purl": "pkg:npm/%40humanwhocodes/object-schema@2.0.3" + }, + { + "name": "@img/colour", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/%40img/colour@1.1.0" + }, + { + "name": "@img/sharp-darwin-arm64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-darwin-arm64@0.35.4" + }, + { + "name": "@img/sharp-darwin-x64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-darwin-x64@0.35.4" + }, + { + "name": "@img/sharp-freebsd-wasm32", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-freebsd-wasm32@0.35.4" + }, + { + "name": "@img/sharp-libvips-darwin-arm64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-darwin-arm64@1.3.3" + }, + { + "name": "@img/sharp-libvips-darwin-x64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-darwin-x64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-arm", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-arm@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-arm64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-arm64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-ppc64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-ppc64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-riscv64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-riscv64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-s390x", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-s390x@1.3.3" + }, + { + "name": "@img/sharp-libvips-linux-x64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linux-x64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linuxmusl-arm64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linuxmusl-arm64@1.3.3" + }, + { + "name": "@img/sharp-libvips-linuxmusl-x64", + "version": "1.3.3", + "license": "LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-libvips-linuxmusl-x64@1.3.3" + }, + { + "name": "@img/sharp-linux-arm", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-arm@0.35.4" + }, + { + "name": "@img/sharp-linux-arm64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-arm64@0.35.4" + }, + { + "name": "@img/sharp-linux-ppc64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-ppc64@0.35.4" + }, + { + "name": "@img/sharp-linux-riscv64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-riscv64@0.35.4" + }, + { + "name": "@img/sharp-linux-s390x", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-s390x@0.35.4" + }, + { + "name": "@img/sharp-linux-x64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linux-x64@0.35.4" + }, + { + "name": "@img/sharp-linuxmusl-arm64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linuxmusl-arm64@0.35.4" + }, + { + "name": "@img/sharp-linuxmusl-x64", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-linuxmusl-x64@0.35.4" + }, + { + "name": "@img/sharp-wasm32", + "version": "0.35.4", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "purl": "pkg:npm/%40img/sharp-wasm32@0.35.4" + }, + { + "name": "@img/sharp-webcontainers-wasm32", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/%40img/sharp-webcontainers-wasm32@0.35.4" + }, + { + "name": "@img/sharp-win32-arm64", + "version": "0.35.4", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-win32-arm64@0.35.4" + }, + { + "name": "@img/sharp-win32-ia32", + "version": "0.35.4", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-win32-ia32@0.35.4" + }, + { + "name": "@img/sharp-win32-x64", + "version": "0.35.4", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "purl": "pkg:npm/%40img/sharp-win32-x64@0.35.4" + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "purl": "pkg:npm/%40jridgewell/resolve-uri@3.1.2" + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.6.0", + "license": "MIT", + "purl": "pkg:npm/%40jridgewell/sourcemap-codec@1.6.0" + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "purl": "pkg:npm/%40jridgewell/trace-mapping@0.3.31" + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "license": "MIT", + "purl": "pkg:npm/%40napi-rs/wasm-runtime@1.2.3" + }, + { + "name": "@next/env", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/env@15.5.25" + }, + { + "name": "@next/eslint-plugin-next", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/eslint-plugin-next@15.5.25" + }, + { + "name": "@next/swc-darwin-arm64", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-darwin-arm64@15.5.25" + }, + { + "name": "@next/swc-darwin-x64", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-darwin-x64@15.5.25" + }, + { + "name": "@next/swc-linux-arm64-gnu", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-linux-arm64-gnu@15.5.25" + }, + { + "name": "@next/swc-linux-arm64-musl", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-linux-arm64-musl@15.5.25" + }, + { + "name": "@next/swc-linux-x64-gnu", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-linux-x64-gnu@15.5.25" + }, + { + "name": "@next/swc-linux-x64-musl", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-linux-x64-musl@15.5.25" + }, + { + "name": "@next/swc-win32-arm64-msvc", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-win32-arm64-msvc@15.5.25" + }, + { + "name": "@next/swc-win32-x64-msvc", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/%40next/swc-win32-x64-msvc@15.5.25" + }, + { + "name": "@nodelib/fs.scandir", + "version": "2.1.5", + "license": "MIT", + "purl": "pkg:npm/%40nodelib/fs.scandir@2.1.5" + }, + { + "name": "@nodelib/fs.stat", + "version": "2.0.5", + "license": "MIT", + "purl": "pkg:npm/%40nodelib/fs.stat@2.0.5" + }, + { + "name": "@nodelib/fs.walk", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40nodelib/fs.walk@1.2.8" + }, + { + "name": "@nolyfill/is-core-module", + "version": "1.0.39", + "license": "MIT", + "purl": "pkg:npm/%40nolyfill/is-core-module@1.0.39" + }, + { + "name": "@oxc-project/types", + "version": "0.149.0", + "license": "MIT", + "purl": "pkg:npm/%40oxc-project/types@0.149.0" + }, + { + "name": "@playwright/test", + "version": "1.63.0", + "license": "Apache-2.0", + "purl": "pkg:npm/%40playwright/test@1.63.0" + }, + { + "name": "@rolldown/binding-android-arm-eabi", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-android-arm-eabi@1.2.8" + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-android-arm64@1.2.8" + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-darwin-arm64@1.2.8" + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-darwin-x64@1.2.8" + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-freebsd-x64@1.2.8" + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-arm-gnueabihf@1.2.8" + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-arm64-gnu@1.2.8" + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-arm64-musl@1.2.8" + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-ppc64-gnu@1.2.8" + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-s390x-gnu@1.2.8" + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-x64-gnu@1.2.8" + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-linux-x64-musl@1.2.8" + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-openharmony-arm64@1.2.8" + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-win32-arm64-msvc@1.2.8" + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/binding-win32-x64-msvc@1.2.8" + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/%40rolldown/pluginutils@1.0.1" + }, + { + "name": "@rtsao/scc", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/%40rtsao/scc@1.1.0" + }, + { + "name": "@rushstack/eslint-patch", + "version": "1.16.1", + "license": "MIT", + "purl": "pkg:npm/%40rushstack/eslint-patch@1.16.1" + }, + { + "name": "@scanly/benchmark", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/benchmark@2.1.0" + }, + { + "name": "@scanly/browser", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/browser@2.1.0" + }, + { + "name": "@scanly/core", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/core@2.1.0" + }, + { + "name": "@scanly/engine-jsqr", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/engine-jsqr@2.1.0" + }, + { + "name": "@scanly/engine-zxing-cpp-wasm", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/engine-zxing-cpp-wasm@2.1.0" + }, + { + "name": "@scanly/engine-zxing-js", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/engine-zxing-js@2.1.0" + }, + { + "name": "@scanly/node", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/node@2.1.0" + }, + { + "name": "@scanly/parsers", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/parsers@2.1.0" + }, + { + "name": "@scanly/react", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/react@2.1.0" + }, + { + "name": "@scanly/scenario-schema", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/scenario-schema@2.1.0" + }, + { + "name": "@scanly/url-safety", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/url-safety@2.1.0" + }, + { + "name": "@scanly/web-demo", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/%40scanly/web-demo@2.1.0" + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/%40standard-schema/spec@1.1.0" + }, + { + "name": "@swc/helpers", + "version": "0.5.15", + "license": "Apache-2.0", + "purl": "pkg:npm/%40swc/helpers@0.5.15" + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "purl": "pkg:npm/%40tybys/wasm-util@0.10.3" + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "purl": "pkg:npm/%40types/chai@5.2.3" + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "purl": "pkg:npm/%40types/deep-eql@4.0.2" + }, + { + "name": "@types/emscripten", + "version": "1.41.5", + "license": "MIT", + "purl": "pkg:npm/%40types/emscripten@1.41.5" + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "purl": "pkg:npm/%40types/estree@1.0.9" + }, + { + "name": "@types/json5", + "version": "0.0.29", + "license": "MIT", + "purl": "pkg:npm/%40types/json5@0.0.29" + }, + { + "name": "@types/node", + "version": "20.19.43", + "license": "MIT", + "purl": "pkg:npm/%40types/node@20.19.43" + }, + { + "name": "@types/prop-types", + "version": "15.7.15", + "license": "MIT", + "purl": "pkg:npm/%40types/prop-types@15.7.15" + }, + { + "name": "@types/qrcode", + "version": "1.5.6", + "license": "MIT", + "purl": "pkg:npm/%40types/qrcode@1.5.6" + }, + { + "name": "@types/react-dom", + "version": "18.3.7", + "license": "MIT", + "purl": "pkg:npm/%40types/react-dom@18.3.7" + }, + { + "name": "@types/react", + "version": "18.3.28", + "license": "MIT", + "purl": "pkg:npm/%40types/react@18.3.28" + }, + { + "name": "@typescript-eslint/eslint-plugin", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/eslint-plugin@8.64.0" + }, + { + "name": "@typescript-eslint/parser", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/parser@8.64.0" + }, + { + "name": "@typescript-eslint/project-service", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/project-service@8.64.0" + }, + { + "name": "@typescript-eslint/scope-manager", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/scope-manager@8.64.0" + }, + { + "name": "@typescript-eslint/tsconfig-utils", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/tsconfig-utils@8.64.0" + }, + { + "name": "@typescript-eslint/type-utils", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/type-utils@8.64.0" + }, + { + "name": "@typescript-eslint/types", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/types@8.64.0" + }, + { + "name": "@typescript-eslint/typescript-estree", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/typescript-estree@8.64.0" + }, + { + "name": "@typescript-eslint/utils", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/utils@8.64.0" + }, + { + "name": "@typescript-eslint/visitor-keys", + "version": "8.64.0", + "license": "MIT", + "purl": "pkg:npm/%40typescript-eslint/visitor-keys@8.64.0" + }, + { + "name": "@ungap/structured-clone", + "version": "1.3.2", + "license": "ISC", + "purl": "pkg:npm/%40ungap/structured-clone@1.3.2" + }, + { + "name": "@unrs/resolver-binding-android-arm-eabi", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-android-arm-eabi@1.12.2" + }, + { + "name": "@unrs/resolver-binding-android-arm64", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-android-arm64@1.12.2" + }, + { + "name": "@unrs/resolver-binding-darwin-arm64", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-darwin-arm64@1.12.2" + }, + { + "name": "@unrs/resolver-binding-darwin-x64", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-darwin-x64@1.12.2" + }, + { + "name": "@unrs/resolver-binding-freebsd-x64", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-freebsd-x64@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-arm-gnueabihf", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm-gnueabihf@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-arm-musleabihf", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm-musleabihf@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-arm64-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm64-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-arm64-musl", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm64-musl@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-loong64-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-loong64-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-loong64-musl", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-loong64-musl@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-ppc64-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-ppc64-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-riscv64-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-musl", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-riscv64-musl@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-s390x-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-s390x-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-x64-gnu", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-x64-gnu@1.12.2" + }, + { + "name": "@unrs/resolver-binding-linux-x64-musl", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-x64-musl@1.12.2" + }, + { + "name": "@unrs/resolver-binding-openharmony-arm64", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-openharmony-arm64@1.12.2" + }, + { + "name": "@unrs/resolver-binding-wasm32-wasi", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-wasm32-wasi@1.12.2" + }, + { + "name": "@unrs/resolver-binding-win32-arm64-msvc", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-arm64-msvc@1.12.2" + }, + { + "name": "@unrs/resolver-binding-win32-ia32-msvc", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-ia32-msvc@1.12.2" + }, + { + "name": "@unrs/resolver-binding-win32-x64-msvc", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-x64-msvc@1.12.2" + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/coverage-v8@4.1.11" + }, + { + "name": "@vitest/expect", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/expect@4.1.11" + }, + { + "name": "@vitest/mocker", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/mocker@4.1.11" + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/pretty-format@4.1.11" + }, + { + "name": "@vitest/runner", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/runner@4.1.11" + }, + { + "name": "@vitest/snapshot", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/snapshot@4.1.11" + }, + { + "name": "@vitest/spy", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/spy@4.1.11" + }, + { + "name": "@vitest/utils", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/%40vitest/utils@4.1.11" + }, + { + "name": "@zxing/library", + "version": "0.21.3", + "license": "MIT", + "purl": "pkg:npm/%40zxing/library@0.21.3" + }, + { + "name": "@zxing/text-encoding", + "version": "0.9.0", + "license": "(Unlicense OR Apache-2.0)", + "purl": "pkg:npm/%40zxing/text-encoding@0.9.0" + }, + { + "name": "acorn-jsx", + "version": "5.3.2", + "license": "MIT", + "purl": "pkg:npm/acorn-jsx@5.3.2" + }, + { + "name": "acorn", + "version": "8.17.0", + "license": "MIT", + "purl": "pkg:npm/acorn@8.17.0" + }, + { + "name": "ajv-formats", + "version": "3.0.1", + "license": "MIT", + "purl": "pkg:npm/ajv-formats@3.0.1" + }, + { + "name": "ajv", + "version": "6.15.0", + "license": "MIT", + "purl": "pkg:npm/ajv@6.15.0" + }, + { + "name": "ajv", + "version": "8.20.0", + "license": "MIT", + "purl": "pkg:npm/ajv@8.20.0" + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "purl": "pkg:npm/ansi-regex@5.0.1" + }, + { + "name": "ansi-styles", + "version": "4.3.0", + "license": "MIT", + "purl": "pkg:npm/ansi-styles@4.3.0" + }, + { + "name": "argparse", + "version": "2.0.1", + "license": "Python-2.0", + "purl": "pkg:npm/argparse@2.0.1" + }, + { + "name": "aria-query", + "version": "5.3.2", + "license": "Apache-2.0", + "purl": "pkg:npm/aria-query@5.3.2" + }, + { + "name": "array-buffer-byte-length", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/array-buffer-byte-length@1.0.2" + }, + { + "name": "array-includes", + "version": "3.1.9", + "license": "MIT", + "purl": "pkg:npm/array-includes@3.1.9" + }, + { + "name": "array.prototype.findlast", + "version": "1.2.5", + "license": "MIT", + "purl": "pkg:npm/array.prototype.findlast@1.2.5" + }, + { + "name": "array.prototype.findlastindex", + "version": "1.2.6", + "license": "MIT", + "purl": "pkg:npm/array.prototype.findlastindex@1.2.6" + }, + { + "name": "array.prototype.flat", + "version": "1.3.3", + "license": "MIT", + "purl": "pkg:npm/array.prototype.flat@1.3.3" + }, + { + "name": "array.prototype.flatmap", + "version": "1.3.3", + "license": "MIT", + "purl": "pkg:npm/array.prototype.flatmap@1.3.3" + }, + { + "name": "array.prototype.tosorted", + "version": "1.1.4", + "license": "MIT", + "purl": "pkg:npm/array.prototype.tosorted@1.1.4" + }, + { + "name": "arraybuffer.prototype.slice", + "version": "1.0.4", + "license": "MIT", + "purl": "pkg:npm/arraybuffer.prototype.slice@1.0.4" + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "purl": "pkg:npm/assertion-error@2.0.1" + }, + { + "name": "ast-types-flow", + "version": "0.0.8", + "license": "MIT", + "purl": "pkg:npm/ast-types-flow@0.0.8" + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.6", + "license": "MIT", + "purl": "pkg:npm/ast-v8-to-istanbul@1.0.6" + }, + { + "name": "async-function", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/async-function@1.0.0" + }, + { + "name": "available-typed-arrays", + "version": "1.0.7", + "license": "MIT", + "purl": "pkg:npm/available-typed-arrays@1.0.7" + }, + { + "name": "axe-core", + "version": "4.13.0", + "license": "MPL-2.0", + "purl": "pkg:npm/axe-core@4.13.0" + }, + { + "name": "axobject-query", + "version": "4.1.0", + "license": "Apache-2.0", + "purl": "pkg:npm/axobject-query@4.1.0" + }, + { + "name": "balanced-match", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/balanced-match@1.0.2" + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "purl": "pkg:npm/balanced-match@4.0.4" + }, + { + "name": "brace-expansion", + "version": "1.1.18", + "license": "MIT", + "purl": "pkg:npm/brace-expansion@1.1.18" + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "purl": "pkg:npm/brace-expansion@5.0.9" + }, + { + "name": "braces", + "version": "3.0.3", + "license": "MIT", + "purl": "pkg:npm/braces@3.0.3" + }, + { + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/call-bind-apply-helpers@1.0.2" + }, + { + "name": "call-bind", + "version": "1.0.9", + "license": "MIT", + "purl": "pkg:npm/call-bind@1.0.9" + }, + { + "name": "call-bound", + "version": "1.0.4", + "license": "MIT", + "purl": "pkg:npm/call-bound@1.0.4" + }, + { + "name": "callsites", + "version": "3.1.0", + "license": "MIT", + "purl": "pkg:npm/callsites@3.1.0" + }, + { + "name": "camelcase", + "version": "5.3.1", + "license": "MIT", + "purl": "pkg:npm/camelcase@5.3.1" + }, + { + "name": "caniuse-lite", + "version": "1.0.30001769", + "license": "CC-BY-4.0", + "purl": "pkg:npm/caniuse-lite@1.0.30001769" + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "purl": "pkg:npm/chai@6.2.2" + }, + { + "name": "chalk", + "version": "4.1.2", + "license": "MIT", + "purl": "pkg:npm/chalk@4.1.2" + }, + { + "name": "client-only", + "version": "0.0.1", + "license": "MIT", + "purl": "pkg:npm/client-only@0.0.1" + }, + { + "name": "cliui", + "version": "6.0.0", + "license": "ISC", + "purl": "pkg:npm/cliui@6.0.0" + }, + { + "name": "color-convert", + "version": "2.0.1", + "license": "MIT", + "purl": "pkg:npm/color-convert@2.0.1" + }, + { + "name": "color-name", + "version": "1.1.4", + "license": "MIT", + "purl": "pkg:npm/color-name@1.1.4" + }, + { + "name": "concat-map", + "version": "0.0.1", + "license": "MIT", + "purl": "pkg:npm/concat-map@0.0.1" + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "purl": "pkg:npm/convert-source-map@2.0.0" + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "purl": "pkg:npm/cross-spawn@7.0.6" + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "purl": "pkg:npm/csstype@3.2.3" + }, + { + "name": "damerau-levenshtein", + "version": "1.0.8", + "license": "BSD-2-Clause", + "purl": "pkg:npm/damerau-levenshtein@1.0.8" + }, + { + "name": "data-view-buffer", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/data-view-buffer@1.0.2" + }, + { + "name": "data-view-byte-length", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/data-view-byte-length@1.0.2" + }, + { + "name": "data-view-byte-offset", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/data-view-byte-offset@1.0.1" + }, + { + "name": "debug", + "version": "3.2.7", + "license": "MIT", + "purl": "pkg:npm/debug@3.2.7" + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "purl": "pkg:npm/debug@4.4.3" + }, + { + "name": "decamelize", + "version": "1.2.0", + "license": "MIT", + "purl": "pkg:npm/decamelize@1.2.0" + }, + { + "name": "deep-is", + "version": "0.1.4", + "license": "MIT", + "purl": "pkg:npm/deep-is@0.1.4" + }, + { + "name": "define-data-property", + "version": "1.1.4", + "license": "MIT", + "purl": "pkg:npm/define-data-property@1.1.4" + }, + { + "name": "define-properties", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/define-properties@1.2.1" + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "purl": "pkg:npm/detect-libc@2.1.2" + }, + { + "name": "dijkstrajs", + "version": "1.0.3", + "license": "MIT", + "purl": "pkg:npm/dijkstrajs@1.0.3" + }, + { + "name": "doctrine", + "version": "2.1.0", + "license": "Apache-2.0", + "purl": "pkg:npm/doctrine@2.1.0" + }, + { + "name": "doctrine", + "version": "3.0.0", + "license": "Apache-2.0", + "purl": "pkg:npm/doctrine@3.0.0" + }, + { + "name": "dunder-proto", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/dunder-proto@1.0.1" + }, + { + "name": "emoji-regex", + "version": "8.0.0", + "license": "MIT", + "purl": "pkg:npm/emoji-regex@8.0.0" + }, + { + "name": "emoji-regex", + "version": "9.2.2", + "license": "MIT", + "purl": "pkg:npm/emoji-regex@9.2.2" + }, + { + "name": "entities", + "version": "6.0.1", + "license": "BSD-2-Clause", + "purl": "pkg:npm/entities@6.0.1" + }, + { + "name": "es-abstract-get", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/es-abstract-get@1.0.0" + }, + { + "name": "es-abstract", + "version": "1.24.2", + "license": "MIT", + "purl": "pkg:npm/es-abstract@1.24.2" + }, + { + "name": "es-define-property", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/es-define-property@1.0.1" + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "purl": "pkg:npm/es-errors@1.3.0" + }, + { + "name": "es-iterator-helpers", + "version": "1.3.3", + "license": "MIT", + "purl": "pkg:npm/es-iterator-helpers@1.3.3" + }, + { + "name": "es-module-lexer", + "version": "2.3.2", + "license": "MIT", + "purl": "pkg:npm/es-module-lexer@2.3.2" + }, + { + "name": "es-object-atoms", + "version": "1.1.2", + "license": "MIT", + "purl": "pkg:npm/es-object-atoms@1.1.2" + }, + { + "name": "es-set-tostringtag", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/es-set-tostringtag@2.1.0" + }, + { + "name": "es-shim-unscopables", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/es-shim-unscopables@1.1.0" + }, + { + "name": "es-to-primitive", + "version": "1.3.1", + "license": "MIT", + "purl": "pkg:npm/es-to-primitive@1.3.1" + }, + { + "name": "esbuild", + "version": "0.28.2", + "license": "MIT", + "purl": "pkg:npm/esbuild@0.28.2" + }, + { + "name": "escape-string-regexp", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/escape-string-regexp@4.0.0" + }, + { + "name": "eslint-config-next", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/eslint-config-next@15.5.25" + }, + { + "name": "eslint-import-resolver-node", + "version": "0.3.10", + "license": "MIT", + "purl": "pkg:npm/eslint-import-resolver-node@0.3.10" + }, + { + "name": "eslint-import-resolver-typescript", + "version": "3.10.1", + "license": "ISC", + "purl": "pkg:npm/eslint-import-resolver-typescript@3.10.1" + }, + { + "name": "eslint-module-utils", + "version": "2.13.0", + "license": "MIT", + "purl": "pkg:npm/eslint-module-utils@2.13.0" + }, + { + "name": "eslint-plugin-import", + "version": "2.32.0", + "license": "MIT", + "purl": "pkg:npm/eslint-plugin-import@2.32.0" + }, + { + "name": "eslint-plugin-jsx-a11y", + "version": "6.10.2", + "license": "MIT", + "purl": "pkg:npm/eslint-plugin-jsx-a11y@6.10.2" + }, + { + "name": "eslint-plugin-react-hooks", + "version": "5.2.0", + "license": "MIT", + "purl": "pkg:npm/eslint-plugin-react-hooks@5.2.0" + }, + { + "name": "eslint-plugin-react", + "version": "7.37.5", + "license": "MIT", + "purl": "pkg:npm/eslint-plugin-react@7.37.5" + }, + { + "name": "eslint-scope", + "version": "7.2.2", + "license": "BSD-2-Clause", + "purl": "pkg:npm/eslint-scope@7.2.2" + }, + { + "name": "eslint-visitor-keys", + "version": "3.4.3", + "license": "Apache-2.0", + "purl": "pkg:npm/eslint-visitor-keys@3.4.3" + }, + { + "name": "eslint-visitor-keys", + "version": "5.0.1", + "license": "Apache-2.0", + "purl": "pkg:npm/eslint-visitor-keys@5.0.1" + }, + { + "name": "eslint", + "version": "8.57.0", + "license": "MIT", + "purl": "pkg:npm/eslint@8.57.0" + }, + { + "name": "espree", + "version": "9.6.1", + "license": "BSD-2-Clause", + "purl": "pkg:npm/espree@9.6.1" + }, + { + "name": "esquery", + "version": "1.7.0", + "license": "BSD-3-Clause", + "purl": "pkg:npm/esquery@1.7.0" + }, + { + "name": "esrecurse", + "version": "4.3.0", + "license": "BSD-2-Clause", + "purl": "pkg:npm/esrecurse@4.3.0" + }, + { + "name": "estraverse", + "version": "5.3.0", + "license": "BSD-2-Clause", + "purl": "pkg:npm/estraverse@5.3.0" + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "purl": "pkg:npm/estree-walker@3.0.3" + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "purl": "pkg:npm/esutils@2.0.3" + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "purl": "pkg:npm/expect-type@1.4.0" + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "purl": "pkg:npm/fast-deep-equal@3.1.3" + }, + { + "name": "fast-glob", + "version": "3.3.1", + "license": "MIT", + "purl": "pkg:npm/fast-glob@3.3.1" + }, + { + "name": "fast-json-stable-stringify", + "version": "2.1.0", + "license": "MIT", + "purl": "pkg:npm/fast-json-stable-stringify@2.1.0" + }, + { + "name": "fast-levenshtein", + "version": "2.0.6", + "license": "MIT", + "purl": "pkg:npm/fast-levenshtein@2.0.6" + }, + { + "name": "fast-uri", + "version": "3.1.6", + "license": "BSD-3-Clause", + "purl": "pkg:npm/fast-uri@3.1.6" + }, + { + "name": "fastq", + "version": "1.20.1", + "license": "ISC", + "purl": "pkg:npm/fastq@1.20.1" + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "purl": "pkg:npm/fdir@6.5.0" + }, + { + "name": "file-entry-cache", + "version": "6.0.1", + "license": "MIT", + "purl": "pkg:npm/file-entry-cache@6.0.1" + }, + { + "name": "fill-range", + "version": "7.1.1", + "license": "MIT", + "purl": "pkg:npm/fill-range@7.1.1" + }, + { + "name": "find-up", + "version": "4.1.0", + "license": "MIT", + "purl": "pkg:npm/find-up@4.1.0" + }, + { + "name": "find-up", + "version": "5.0.0", + "license": "MIT", + "purl": "pkg:npm/find-up@5.0.0" + }, + { + "name": "flat-cache", + "version": "3.2.0", + "license": "MIT", + "purl": "pkg:npm/flat-cache@3.2.0" + }, + { + "name": "flatted", + "version": "3.4.2", + "license": "ISC", + "purl": "pkg:npm/flatted@3.4.2" + }, + { + "name": "for-each", + "version": "0.3.5", + "license": "MIT", + "purl": "pkg:npm/for-each@0.3.5" + }, + { + "name": "fs.realpath", + "version": "1.0.0", + "license": "ISC", + "purl": "pkg:npm/fs.realpath@1.0.0" + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "purl": "pkg:npm/fsevents@2.3.3" + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "purl": "pkg:npm/function-bind@1.1.2" + }, + { + "name": "function.prototype.name", + "version": "1.2.0", + "license": "MIT", + "purl": "pkg:npm/function.prototype.name@1.2.0" + }, + { + "name": "functions-have-names", + "version": "1.2.3", + "license": "MIT", + "purl": "pkg:npm/functions-have-names@1.2.3" + }, + { + "name": "generator-function", + "version": "2.0.1", + "license": "MIT", + "purl": "pkg:npm/generator-function@2.0.1" + }, + { + "name": "get-caller-file", + "version": "2.0.5", + "license": "ISC", + "purl": "pkg:npm/get-caller-file@2.0.5" + }, + { + "name": "get-intrinsic", + "version": "1.3.0", + "license": "MIT", + "purl": "pkg:npm/get-intrinsic@1.3.0" + }, + { + "name": "get-proto", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/get-proto@1.0.1" + }, + { + "name": "get-symbol-description", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/get-symbol-description@1.1.0" + }, + { + "name": "get-tsconfig", + "version": "4.14.0", + "license": "MIT", + "purl": "pkg:npm/get-tsconfig@4.14.0" + }, + { + "name": "glob-parent", + "version": "5.1.2", + "license": "ISC", + "purl": "pkg:npm/glob-parent@5.1.2" + }, + { + "name": "glob-parent", + "version": "6.0.2", + "license": "ISC", + "purl": "pkg:npm/glob-parent@6.0.2" + }, + { + "name": "glob", + "version": "7.2.3", + "license": "ISC", + "purl": "pkg:npm/glob@7.2.3" + }, + { + "name": "globals", + "version": "13.24.0", + "license": "MIT", + "purl": "pkg:npm/globals@13.24.0" + }, + { + "name": "globalthis", + "version": "1.0.4", + "license": "MIT", + "purl": "pkg:npm/globalthis@1.0.4" + }, + { + "name": "gopd", + "version": "1.2.0", + "license": "MIT", + "purl": "pkg:npm/gopd@1.2.0" + }, + { + "name": "graphemer", + "version": "1.4.0", + "license": "MIT", + "purl": "pkg:npm/graphemer@1.4.0" + }, + { + "name": "has-bigints", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/has-bigints@1.1.0" + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/has-flag@4.0.0" + }, + { + "name": "has-property-descriptors", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/has-property-descriptors@1.0.2" + }, + { + "name": "has-proto", + "version": "1.2.0", + "license": "MIT", + "purl": "pkg:npm/has-proto@1.2.0" + }, + { + "name": "has-symbols", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/has-symbols@1.1.0" + }, + { + "name": "has-tostringtag", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/has-tostringtag@1.0.2" + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "purl": "pkg:npm/hasown@2.0.4" + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "purl": "pkg:npm/html-escaper@2.0.2" + }, + { + "name": "ignore", + "version": "5.3.2", + "license": "MIT", + "purl": "pkg:npm/ignore@5.3.2" + }, + { + "name": "ignore", + "version": "7.0.6", + "license": "MIT", + "purl": "pkg:npm/ignore@7.0.6" + }, + { + "name": "import-fresh", + "version": "3.3.1", + "license": "MIT", + "purl": "pkg:npm/import-fresh@3.3.1" + }, + { + "name": "imurmurhash", + "version": "0.1.4", + "license": "MIT", + "purl": "pkg:npm/imurmurhash@0.1.4" + }, + { + "name": "inflight", + "version": "1.0.6", + "license": "ISC", + "purl": "pkg:npm/inflight@1.0.6" + }, + { + "name": "inherits", + "version": "2.0.4", + "license": "ISC", + "purl": "pkg:npm/inherits@2.0.4" + }, + { + "name": "internal-slot", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/internal-slot@1.1.0" + }, + { + "name": "ipaddr.js", + "version": "2.2.0", + "license": "MIT", + "purl": "pkg:npm/ipaddr.js@2.2.0" + }, + { + "name": "is-array-buffer", + "version": "3.0.5", + "license": "MIT", + "purl": "pkg:npm/is-array-buffer@3.0.5" + }, + { + "name": "is-async-function", + "version": "2.1.1", + "license": "MIT", + "purl": "pkg:npm/is-async-function@2.1.1" + }, + { + "name": "is-bigint", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/is-bigint@1.1.0" + }, + { + "name": "is-boolean-object", + "version": "1.2.2", + "license": "MIT", + "purl": "pkg:npm/is-boolean-object@1.2.2" + }, + { + "name": "is-bun-module", + "version": "2.0.0", + "license": "MIT", + "purl": "pkg:npm/is-bun-module@2.0.0" + }, + { + "name": "is-callable", + "version": "1.2.7", + "license": "MIT", + "purl": "pkg:npm/is-callable@1.2.7" + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "purl": "pkg:npm/is-core-module@2.16.2" + }, + { + "name": "is-data-view", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/is-data-view@1.0.2" + }, + { + "name": "is-date-object", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/is-date-object@1.1.0" + }, + { + "name": "is-document.all", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/is-document.all@1.0.0" + }, + { + "name": "is-extglob", + "version": "2.1.1", + "license": "MIT", + "purl": "pkg:npm/is-extglob@2.1.1" + }, + { + "name": "is-finalizationregistry", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/is-finalizationregistry@1.1.1" + }, + { + "name": "is-fullwidth-code-point", + "version": "3.0.0", + "license": "MIT", + "purl": "pkg:npm/is-fullwidth-code-point@3.0.0" + }, + { + "name": "is-generator-function", + "version": "1.1.2", + "license": "MIT", + "purl": "pkg:npm/is-generator-function@1.1.2" + }, + { + "name": "is-glob", + "version": "4.0.3", + "license": "MIT", + "purl": "pkg:npm/is-glob@4.0.3" + }, + { + "name": "is-map", + "version": "2.0.3", + "license": "MIT", + "purl": "pkg:npm/is-map@2.0.3" + }, + { + "name": "is-negative-zero", + "version": "2.0.3", + "license": "MIT", + "purl": "pkg:npm/is-negative-zero@2.0.3" + }, + { + "name": "is-number-object", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/is-number-object@1.1.1" + }, + { + "name": "is-number", + "version": "7.0.0", + "license": "MIT", + "purl": "pkg:npm/is-number@7.0.0" + }, + { + "name": "is-path-inside", + "version": "3.0.3", + "license": "MIT", + "purl": "pkg:npm/is-path-inside@3.0.3" + }, + { + "name": "is-regex", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/is-regex@1.2.1" + }, + { + "name": "is-set", + "version": "2.0.3", + "license": "MIT", + "purl": "pkg:npm/is-set@2.0.3" + }, + { + "name": "is-shared-array-buffer", + "version": "1.0.4", + "license": "MIT", + "purl": "pkg:npm/is-shared-array-buffer@1.0.4" + }, + { + "name": "is-string", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/is-string@1.1.1" + }, + { + "name": "is-symbol", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/is-symbol@1.1.1" + }, + { + "name": "is-typed-array", + "version": "1.1.15", + "license": "MIT", + "purl": "pkg:npm/is-typed-array@1.1.15" + }, + { + "name": "is-weakmap", + "version": "2.0.2", + "license": "MIT", + "purl": "pkg:npm/is-weakmap@2.0.2" + }, + { + "name": "is-weakref", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/is-weakref@1.1.1" + }, + { + "name": "is-weakset", + "version": "2.0.4", + "license": "MIT", + "purl": "pkg:npm/is-weakset@2.0.4" + }, + { + "name": "isarray", + "version": "2.0.5", + "license": "MIT", + "purl": "pkg:npm/isarray@2.0.5" + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "purl": "pkg:npm/isexe@2.0.0" + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "purl": "pkg:npm/istanbul-lib-coverage@3.2.2" + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "purl": "pkg:npm/istanbul-lib-report@3.0.1" + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "purl": "pkg:npm/istanbul-reports@3.2.0" + }, + { + "name": "iterator.prototype", + "version": "1.1.5", + "license": "MIT", + "purl": "pkg:npm/iterator.prototype@1.1.5" + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "purl": "pkg:npm/js-tokens@10.0.0" + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/js-tokens@4.0.0" + }, + { + "name": "js-yaml", + "version": "4.3.2", + "license": "MIT", + "purl": "pkg:npm/js-yaml@4.3.2" + }, + { + "name": "js-yaml", + "version": "5.4.1", + "license": "MIT", + "purl": "pkg:npm/js-yaml@5.4.1" + }, + { + "name": "json-buffer", + "version": "3.0.1", + "license": "MIT", + "purl": "pkg:npm/json-buffer@3.0.1" + }, + { + "name": "json-schema-traverse", + "version": "0.4.1", + "license": "MIT", + "purl": "pkg:npm/json-schema-traverse@0.4.1" + }, + { + "name": "json-schema-traverse", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/json-schema-traverse@1.0.0" + }, + { + "name": "json-stable-stringify-without-jsonify", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/json-stable-stringify-without-jsonify@1.0.1" + }, + { + "name": "json5", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/json5@1.0.2" + }, + { + "name": "jsqr", + "version": "1.4.0", + "license": "Apache-2.0", + "purl": "pkg:npm/jsqr@1.4.0" + }, + { + "name": "jsx-ast-utils", + "version": "3.3.5", + "license": "MIT", + "purl": "pkg:npm/jsx-ast-utils@3.3.5" + }, + { + "name": "keyv", + "version": "4.5.4", + "license": "MIT", + "purl": "pkg:npm/keyv@4.5.4" + }, + { + "name": "language-subtag-registry", + "version": "0.3.23", + "license": "CC0-1.0", + "purl": "pkg:npm/language-subtag-registry@0.3.23" + }, + { + "name": "language-tags", + "version": "1.0.9", + "license": "MIT", + "purl": "pkg:npm/language-tags@1.0.9" + }, + { + "name": "levn", + "version": "0.4.1", + "license": "MIT", + "purl": "pkg:npm/levn@0.4.1" + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-android-arm64@1.33.0" + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-darwin-arm64@1.33.0" + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-darwin-x64@1.33.0" + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-freebsd-x64@1.33.0" + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-linux-arm-gnueabihf@1.33.0" + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-linux-arm64-gnu@1.33.0" + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-linux-arm64-musl@1.33.0" + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-linux-x64-gnu@1.33.0" + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-linux-x64-musl@1.33.0" + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-win32-arm64-msvc@1.33.0" + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss-win32-x64-msvc@1.33.0" + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "purl": "pkg:npm/lightningcss@1.33.0" + }, + { + "name": "locate-path", + "version": "5.0.0", + "license": "MIT", + "purl": "pkg:npm/locate-path@5.0.0" + }, + { + "name": "locate-path", + "version": "6.0.0", + "license": "MIT", + "purl": "pkg:npm/locate-path@6.0.0" + }, + { + "name": "lodash.merge", + "version": "4.6.2", + "license": "MIT", + "purl": "pkg:npm/lodash.merge@4.6.2" + }, + { + "name": "loose-envify", + "version": "1.4.0", + "license": "MIT", + "purl": "pkg:npm/loose-envify@1.4.0" + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "purl": "pkg:npm/magic-string@0.30.21" + }, + { + "name": "magicast", + "version": "0.5.4", + "license": "MIT", + "purl": "pkg:npm/magicast@0.5.4" + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/make-dir@4.0.0" + }, + { + "name": "math-intrinsics", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/math-intrinsics@1.1.0" + }, + { + "name": "merge2", + "version": "1.4.1", + "license": "MIT", + "purl": "pkg:npm/merge2@1.4.1" + }, + { + "name": "micromatch", + "version": "4.0.8", + "license": "MIT", + "purl": "pkg:npm/micromatch@4.0.8" + }, + { + "name": "minimatch", + "version": "10.2.5", + "license": "BlueOak-1.0.0", + "purl": "pkg:npm/minimatch@10.2.5" + }, + { + "name": "minimatch", + "version": "3.1.5", + "license": "ISC", + "purl": "pkg:npm/minimatch@3.1.5" + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/minimist@1.2.8" + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "purl": "pkg:npm/ms@2.1.3" + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "purl": "pkg:npm/nanoid@3.3.18" + }, + { + "name": "napi-postinstall", + "version": "0.3.4", + "license": "MIT", + "purl": "pkg:npm/napi-postinstall@0.3.4" + }, + { + "name": "natural-compare", + "version": "1.4.0", + "license": "MIT", + "purl": "pkg:npm/natural-compare@1.4.0" + }, + { + "name": "next", + "version": "15.5.25", + "license": "MIT", + "purl": "pkg:npm/next@15.5.25" + }, + { + "name": "node-exports-info", + "version": "1.6.2", + "license": "MIT", + "purl": "pkg:npm/node-exports-info@1.6.2" + }, + { + "name": "object-assign", + "version": "4.1.1", + "license": "MIT", + "purl": "pkg:npm/object-assign@4.1.1" + }, + { + "name": "object-inspect", + "version": "1.13.4", + "license": "MIT", + "purl": "pkg:npm/object-inspect@1.13.4" + }, + { + "name": "object-keys", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/object-keys@1.1.1" + }, + { + "name": "object.assign", + "version": "4.1.7", + "license": "MIT", + "purl": "pkg:npm/object.assign@4.1.7" + }, + { + "name": "object.entries", + "version": "1.1.9", + "license": "MIT", + "purl": "pkg:npm/object.entries@1.1.9" + }, + { + "name": "object.fromentries", + "version": "2.0.8", + "license": "MIT", + "purl": "pkg:npm/object.fromentries@2.0.8" + }, + { + "name": "object.groupby", + "version": "1.0.3", + "license": "MIT", + "purl": "pkg:npm/object.groupby@1.0.3" + }, + { + "name": "object.values", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/object.values@1.2.1" + }, + { + "name": "obug", + "version": "2.2.1", + "license": "MIT", + "purl": "pkg:npm/obug@2.2.1" + }, + { + "name": "once", + "version": "1.4.0", + "license": "ISC", + "purl": "pkg:npm/once@1.4.0" + }, + { + "name": "optionator", + "version": "0.9.4", + "license": "MIT", + "purl": "pkg:npm/optionator@0.9.4" + }, + { + "name": "own-keys", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/own-keys@1.0.1" + }, + { + "name": "p-limit", + "version": "2.3.0", + "license": "MIT", + "purl": "pkg:npm/p-limit@2.3.0" + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "purl": "pkg:npm/p-limit@3.1.0" + }, + { + "name": "p-locate", + "version": "4.1.0", + "license": "MIT", + "purl": "pkg:npm/p-locate@4.1.0" + }, + { + "name": "p-locate", + "version": "5.0.0", + "license": "MIT", + "purl": "pkg:npm/p-locate@5.0.0" + }, + { + "name": "p-try", + "version": "2.2.0", + "license": "MIT", + "purl": "pkg:npm/p-try@2.2.0" + }, + { + "name": "parent-module", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/parent-module@1.0.1" + }, + { + "name": "parse5", + "version": "7.3.0", + "license": "MIT", + "purl": "pkg:npm/parse5@7.3.0" + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/path-exists@4.0.0" + }, + { + "name": "path-is-absolute", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/path-is-absolute@1.0.1" + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "purl": "pkg:npm/path-key@3.1.1" + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "purl": "pkg:npm/path-parse@1.0.7" + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "purl": "pkg:npm/pathe@2.0.3" + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "purl": "pkg:npm/picocolors@1.1.1" + }, + { + "name": "picomatch", + "version": "2.3.2", + "license": "MIT", + "purl": "pkg:npm/picomatch@2.3.2" + }, + { + "name": "picomatch", + "version": "4.0.4", + "license": "MIT", + "purl": "pkg:npm/picomatch@4.0.4" + }, + { + "name": "picomatch", + "version": "4.0.7", + "license": "MIT", + "purl": "pkg:npm/picomatch@4.0.7" + }, + { + "name": "playwright-core", + "version": "1.63.0", + "license": "Apache-2.0", + "purl": "pkg:npm/playwright-core@1.63.0" + }, + { + "name": "playwright", + "version": "1.63.0", + "license": "Apache-2.0", + "purl": "pkg:npm/playwright@1.63.0" + }, + { + "name": "pngjs", + "version": "5.0.0", + "license": "MIT", + "purl": "pkg:npm/pngjs@5.0.0" + }, + { + "name": "possible-typed-array-names", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/possible-typed-array-names@1.1.0" + }, + { + "name": "postcss", + "version": "8.5.25", + "license": "MIT", + "purl": "pkg:npm/postcss@8.5.25" + }, + { + "name": "prelude-ls", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/prelude-ls@1.2.1" + }, + { + "name": "prop-types", + "version": "15.8.1", + "license": "MIT", + "purl": "pkg:npm/prop-types@15.8.1" + }, + { + "name": "punycode", + "version": "2.3.1", + "license": "MIT", + "purl": "pkg:npm/punycode@2.3.1" + }, + { + "name": "qrcode", + "version": "1.5.4", + "license": "MIT", + "purl": "pkg:npm/qrcode@1.5.4" + }, + { + "name": "queue-microtask", + "version": "1.2.3", + "license": "MIT", + "purl": "pkg:npm/queue-microtask@1.2.3" + }, + { + "name": "react-dom", + "version": "18.3.1", + "license": "MIT", + "purl": "pkg:npm/react-dom@18.3.1" + }, + { + "name": "react-is", + "version": "16.13.1", + "license": "MIT", + "purl": "pkg:npm/react-is@16.13.1" + }, + { + "name": "react", + "version": "18.3.1", + "license": "MIT", + "purl": "pkg:npm/react@18.3.1" + }, + { + "name": "reflect.getprototypeof", + "version": "1.0.10", + "license": "MIT", + "purl": "pkg:npm/reflect.getprototypeof@1.0.10" + }, + { + "name": "regexp.prototype.flags", + "version": "1.5.4", + "license": "MIT", + "purl": "pkg:npm/regexp.prototype.flags@1.5.4" + }, + { + "name": "require-directory", + "version": "2.1.1", + "license": "MIT", + "purl": "pkg:npm/require-directory@2.1.1" + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "purl": "pkg:npm/require-from-string@2.0.2" + }, + { + "name": "require-main-filename", + "version": "2.0.0", + "license": "ISC", + "purl": "pkg:npm/require-main-filename@2.0.0" + }, + { + "name": "resolve-from", + "version": "4.0.0", + "license": "MIT", + "purl": "pkg:npm/resolve-from@4.0.0" + }, + { + "name": "resolve-pkg-maps", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/resolve-pkg-maps@1.0.0" + }, + { + "name": "resolve", + "version": "2.0.0-next.7", + "license": "MIT", + "purl": "pkg:npm/resolve@2.0.0-next.7" + }, + { + "name": "reusify", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/reusify@1.1.0" + }, + { + "name": "rimraf", + "version": "3.0.2", + "license": "ISC", + "purl": "pkg:npm/rimraf@3.0.2" + }, + { + "name": "rolldown", + "version": "1.2.8", + "license": "MIT", + "purl": "pkg:npm/rolldown@1.2.8" + }, + { + "name": "run-parallel", + "version": "1.2.0", + "license": "MIT", + "purl": "pkg:npm/run-parallel@1.2.0" + }, + { + "name": "safe-array-concat", + "version": "1.1.4", + "license": "MIT", + "purl": "pkg:npm/safe-array-concat@1.1.4" + }, + { + "name": "safe-push-apply", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/safe-push-apply@1.0.0" + }, + { + "name": "safe-regex-test", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/safe-regex-test@1.1.0" + }, + { + "name": "scheduler", + "version": "0.23.2", + "license": "MIT", + "purl": "pkg:npm/scheduler@0.23.2" + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "purl": "pkg:npm/semver@6.3.1" + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "purl": "pkg:npm/semver@7.8.5" + }, + { + "name": "set-blocking", + "version": "2.0.0", + "license": "ISC", + "purl": "pkg:npm/set-blocking@2.0.0" + }, + { + "name": "set-function-length", + "version": "1.2.2", + "license": "MIT", + "purl": "pkg:npm/set-function-length@1.2.2" + }, + { + "name": "set-function-name", + "version": "2.0.2", + "license": "MIT", + "purl": "pkg:npm/set-function-name@2.0.2" + }, + { + "name": "set-proto", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/set-proto@1.0.0" + }, + { + "name": "sharp", + "version": "0.35.4", + "license": "Apache-2.0", + "purl": "pkg:npm/sharp@0.35.4" + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "purl": "pkg:npm/shebang-command@2.0.0" + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "purl": "pkg:npm/shebang-regex@3.0.0" + }, + { + "name": "side-channel-list", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/side-channel-list@1.0.1" + }, + { + "name": "side-channel-map", + "version": "1.0.1", + "license": "MIT", + "purl": "pkg:npm/side-channel-map@1.0.1" + }, + { + "name": "side-channel-weakmap", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/side-channel-weakmap@1.0.2" + }, + { + "name": "side-channel", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/side-channel@1.1.1" + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "purl": "pkg:npm/siginfo@2.0.0" + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "purl": "pkg:npm/source-map-js@1.2.1" + }, + { + "name": "stable-hash", + "version": "0.0.5", + "license": "MIT", + "purl": "pkg:npm/stable-hash@0.0.5" + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "purl": "pkg:npm/stackback@0.0.2" + }, + { + "name": "std-env", + "version": "4.2.0", + "license": "MIT", + "purl": "pkg:npm/std-env@4.2.0" + }, + { + "name": "stop-iteration-iterator", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/stop-iteration-iterator@1.1.0" + }, + { + "name": "string-width", + "version": "4.2.3", + "license": "MIT", + "purl": "pkg:npm/string-width@4.2.3" + }, + { + "name": "string.prototype.includes", + "version": "2.0.1", + "license": "MIT", + "purl": "pkg:npm/string.prototype.includes@2.0.1" + }, + { + "name": "string.prototype.matchall", + "version": "4.0.12", + "license": "MIT", + "purl": "pkg:npm/string.prototype.matchall@4.0.12" + }, + { + "name": "string.prototype.repeat", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/string.prototype.repeat@1.0.0" + }, + { + "name": "string.prototype.trim", + "version": "1.2.11", + "license": "MIT", + "purl": "pkg:npm/string.prototype.trim@1.2.11" + }, + { + "name": "string.prototype.trimend", + "version": "1.0.10", + "license": "MIT", + "purl": "pkg:npm/string.prototype.trimend@1.0.10" + }, + { + "name": "string.prototype.trimstart", + "version": "1.0.8", + "license": "MIT", + "purl": "pkg:npm/string.prototype.trimstart@1.0.8" + }, + { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "purl": "pkg:npm/strip-ansi@6.0.1" + }, + { + "name": "strip-bom", + "version": "3.0.0", + "license": "MIT", + "purl": "pkg:npm/strip-bom@3.0.0" + }, + { + "name": "strip-json-comments", + "version": "3.1.1", + "license": "MIT", + "purl": "pkg:npm/strip-json-comments@3.1.1" + }, + { + "name": "styled-jsx", + "version": "5.1.6", + "license": "MIT", + "purl": "pkg:npm/styled-jsx@5.1.6" + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "purl": "pkg:npm/supports-color@7.2.0" + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/supports-preserve-symlinks-flag@1.0.0" + }, + { + "name": "tagged-tag", + "version": "1.0.0", + "license": "MIT", + "purl": "pkg:npm/tagged-tag@1.0.0" + }, + { + "name": "text-table", + "version": "0.2.0", + "license": "MIT", + "purl": "pkg:npm/text-table@0.2.0" + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "purl": "pkg:npm/tinybench@2.9.0" + }, + { + "name": "tinyexec", + "version": "1.3.1", + "license": "MIT", + "purl": "pkg:npm/tinyexec@1.3.1" + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "purl": "pkg:npm/tinyglobby@0.2.17" + }, + { + "name": "tinyrainbow", + "version": "3.1.1", + "license": "MIT", + "purl": "pkg:npm/tinyrainbow@3.1.1" + }, + { + "name": "to-regex-range", + "version": "5.0.1", + "license": "MIT", + "purl": "pkg:npm/to-regex-range@5.0.1" + }, + { + "name": "ts-api-utils", + "version": "2.5.0", + "license": "MIT", + "purl": "pkg:npm/ts-api-utils@2.5.0" + }, + { + "name": "ts-custom-error", + "version": "3.3.1", + "license": "MIT", + "purl": "pkg:npm/ts-custom-error@3.3.1" + }, + { + "name": "tsconfig-paths", + "version": "3.15.0", + "license": "MIT", + "purl": "pkg:npm/tsconfig-paths@3.15.0" + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "purl": "pkg:npm/tslib@2.8.1" + }, + { + "name": "tsx", + "version": "4.23.13", + "license": "MIT", + "purl": "pkg:npm/tsx@4.23.13" + }, + { + "name": "type-check", + "version": "0.4.0", + "license": "MIT", + "purl": "pkg:npm/type-check@0.4.0" + }, + { + "name": "type-fest", + "version": "0.20.2", + "license": "(MIT OR CC0-1.0)", + "purl": "pkg:npm/type-fest@0.20.2" + }, + { + "name": "type-fest", + "version": "5.8.0", + "license": "(MIT OR CC0-1.0)", + "purl": "pkg:npm/type-fest@5.8.0" + }, + { + "name": "typed-array-buffer", + "version": "1.0.3", + "license": "MIT", + "purl": "pkg:npm/typed-array-buffer@1.0.3" + }, + { + "name": "typed-array-byte-length", + "version": "1.0.3", + "license": "MIT", + "purl": "pkg:npm/typed-array-byte-length@1.0.3" + }, + { + "name": "typed-array-byte-offset", + "version": "1.0.4", + "license": "MIT", + "purl": "pkg:npm/typed-array-byte-offset@1.0.4" + }, + { + "name": "typed-array-length", + "version": "1.0.8", + "license": "MIT", + "purl": "pkg:npm/typed-array-length@1.0.8" + }, + { + "name": "typescript", + "version": "5.9.3", + "license": "Apache-2.0", + "purl": "pkg:npm/typescript@5.9.3" + }, + { + "name": "unbox-primitive", + "version": "1.1.0", + "license": "MIT", + "purl": "pkg:npm/unbox-primitive@1.1.0" + }, + { + "name": "undici-types", + "version": "6.21.0", + "license": "MIT", + "purl": "pkg:npm/undici-types@6.21.0" + }, + { + "name": "unrs-resolver", + "version": "1.12.2", + "license": "MIT", + "purl": "pkg:npm/unrs-resolver@1.12.2" + }, + { + "name": "uri-js", + "version": "4.4.1", + "license": "BSD-2-Clause", + "purl": "pkg:npm/uri-js@4.4.1" + }, + { + "name": "vite", + "version": "8.2.2", + "license": "MIT", + "purl": "pkg:npm/vite@8.2.2" + }, + { + "name": "vitest", + "version": "4.1.11", + "license": "MIT", + "purl": "pkg:npm/vitest@4.1.11" + }, + { + "name": "which-boxed-primitive", + "version": "1.1.1", + "license": "MIT", + "purl": "pkg:npm/which-boxed-primitive@1.1.1" + }, + { + "name": "which-builtin-type", + "version": "1.2.1", + "license": "MIT", + "purl": "pkg:npm/which-builtin-type@1.2.1" + }, + { + "name": "which-collection", + "version": "1.0.2", + "license": "MIT", + "purl": "pkg:npm/which-collection@1.0.2" + }, + { + "name": "which-module", + "version": "2.0.1", + "license": "ISC", + "purl": "pkg:npm/which-module@2.0.1" + }, + { + "name": "which-typed-array", + "version": "1.1.22", + "license": "MIT", + "purl": "pkg:npm/which-typed-array@1.1.22" + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "purl": "pkg:npm/which@2.0.2" + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "purl": "pkg:npm/why-is-node-running@2.3.0" + }, + { + "name": "word-wrap", + "version": "1.2.5", + "license": "MIT", + "purl": "pkg:npm/word-wrap@1.2.5" + }, + { + "name": "wrap-ansi", + "version": "6.2.0", + "license": "MIT", + "purl": "pkg:npm/wrap-ansi@6.2.0" + }, + { + "name": "wrappy", + "version": "1.0.2", + "license": "ISC", + "purl": "pkg:npm/wrappy@1.0.2" + }, + { + "name": "y18n", + "version": "4.0.3", + "license": "ISC", + "purl": "pkg:npm/y18n@4.0.3" + }, + { + "name": "yargs-parser", + "version": "18.1.3", + "license": "ISC", + "purl": "pkg:npm/yargs-parser@18.1.3" + }, + { + "name": "yargs", + "version": "15.4.1", + "license": "MIT", + "purl": "pkg:npm/yargs@15.4.1" + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "purl": "pkg:npm/yocto-queue@0.1.0" + }, + { + "name": "zxing-wasm", + "version": "3.1.1", + "license": "MIT", + "purl": "pkg:npm/zxing-wasm@3.1.1" + }, + { + "name": "zxing-cpp", + "version": "6c2961d2a9ea4bc4e4ae8f37b1497299f04dd861", + "license": "Apache-2.0", + "purl": "pkg:github/zxing-cpp/zxing-cpp@6c2961d2a9ea4bc4e4ae8f37b1497299f04dd861" + }, + { + "name": "androidx.camera", + "version": "1.5.0", + "license": "Apache-2.0", + "purl": "pkg:maven/androidx.camera/camera-core@1.5.0" + }, + { + "name": "androidx.lifecycle", + "version": "2.9.2", + "license": "Apache-2.0", + "purl": "pkg:maven/androidx.lifecycle/lifecycle-runtime-ktx@2.9.2" + }, + { + "name": "androidx.core", + "version": "1.17.0", + "license": "Apache-2.0", + "purl": "pkg:maven/androidx.core/core-ktx@1.17.0" + } + ] +} diff --git a/release/stable/v2.1.0/physical-validation-status.json b/release/stable/v2.1.0/physical-validation-status.json new file mode 100644 index 0000000..fb1d9e9 --- /dev/null +++ b/release/stable/v2.1.0/physical-validation-status.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": "scanly-stable-physical-validation-status-1", + "version": "2.1.0", + "status": "POST_RELEASE_VALIDATION_REQUIRED", + "fullDeviceMatrix": "POST_RELEASE_VALIDATION_PENDING", + "requiredForPublication": false, + "issue": 13, + "issueScope": "V2.0.0_POST_RELEASE_PHYSICAL_QUALIFICATION", + "releaseStatement": "No physical-device PASS is claimed for v2.1.0.", + "physicalMobileDeviceCount": 0, + "iosSafariSessionCount": 0, + "androidChromeSessionCount": 0, + "physicalLongSessionCount": 0, + "nativeIosPhysicalSessionCount": 0, + "nativeAndroidPhysicalSessionCount": 0, + "matrix": { + "webIos": "POST_RELEASE_VALIDATION_PENDING", + "webAndroid": "POST_RELEASE_VALIDATION_PENDING", + "nativeIos": "POST_RELEASE_VALIDATION_PENDING", + "nativeAndroid": "POST_RELEASE_VALIDATION_PENDING", + "cameraSoak30Minutes": "POST_RELEASE_VALIDATION_PENDING", + "extendedSoak60Minutes": "POST_RELEASE_VALIDATION_PENDING", + "fullDeviceMatrix": "POST_RELEASE_VALIDATION_PENDING" + }, + "evidencePolicy": "NOT_TESTED is never promoted to PASS without admissible physical evidence." +} diff --git a/release/stable/v2.1.0/publication-credentials.json b/release/stable/v2.1.0/publication-credentials.json new file mode 100644 index 0000000..36f03fc --- /dev/null +++ b/release/stable/v2.1.0/publication-credentials.json @@ -0,0 +1,108 @@ +{ + "schemaVersion": "scanly-stable-publication-credentials-1", + "version": "2.1.0", + "secretMaterialCommitted": false, + "channels": { + "githubApi": { + "required": true, + "status": "AVAILABLE", + "evidence": "Authenticated gh session; secret value not recorded" + }, + "productionTagSigningIdentity": { + "required": true, + "status": "AVAILABLE", + "evidence": { + "scheme": "SSH_ED25519", + "githubAccount": "Yangjunjie-Lin", + "githubSigningKeyId": 1118906, + "publicKeyFingerprint": "SHA256:1odyhkHV3hKHFlE8QrELlBGMM/qlM5ftmiiVym/M6ck", + "publicKeyRegisteredAt": "2026-08-18T01:00:29.903+01:00", + "localSmokeTest": { + "status": "PASS", + "annotatedTagObjectCreated": true, + "sshSignatureBlockPresent": true, + "temporaryTagDeleted": true, + "tagObject": "60d17d680d457103be803e2e0f789320b1a00521" + } + } + }, + "npmRegistry": { + "required": true, + "status": "AVAILABLE", + "evidence": { + "registry": "https://registry.npmjs.org/", + "account": "yangjunjielin", + "organization": "scanly", + "organizationRole": "owner", + "trustedPublisherStatus": "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", + "trustedPublisherPackageCount": 10, + "trustedPublisherPackages": [ + "@scanly/parsers", + "@scanly/scenario-schema", + "@scanly/benchmark", + "@scanly/core", + "@scanly/engine-jsqr", + "@scanly/engine-zxing-js", + "@scanly/engine-zxing-cpp-wasm", + "@scanly/browser", + "@scanly/node", + "@scanly/react" + ], + "repository": "Yangjunjie-Lin/Scanly", + "workflowFile": "stable-npm-publish.yml", + "secretValueRecorded": false, + "provenanceWorkflow": ".github/workflows/stable-npm-publish.yml", + "provenanceMechanism": "GITHUB_ACTIONS_OIDC", + "qualificationRun": 34723767114, + "bootstrap": { + "package": "@scanly/url-safety", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "method": "INTERACTIVE_NPM_PROVENANCE_FILE", + "cliAuthenticatedAccount": "yangjunjielin", + "interactiveTwoFactorRequired": true, + "npmCredentialSharedWithCI": false, + "provenanceVerified": true, + "signedReleaseTagRequired": true, + "placeholderVersionAllowed": false, + "artifactPath": "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", + "provenancePath": "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "provenanceSha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87" + } + } + }, + "npmProvenance": { + "required": true, + "status": "AVAILABLE", + "workflow": ".github/workflows/stable-npm-publish.yml", + "mechanism": "GITHUB_ACTIONS_OIDC", + "githubActionsIdTokenPermission": "write" + }, + "androidAarGitHubRelease": { + "required": true, + "status": "AVAILABLE" + }, + "mavenCentral": { + "required": false, + "status": "NOT_REQUIRED_FOR_THIS_RELEASE", + "distribution": "ANDROID_AAR_GITHUB_RELEASE_ONLY" + }, + "iosSpm": { + "required": true, + "status": "AVAILABLE_AFTER_SIGNED_TAG", + "distribution": "SIGNED_GIT_TAG_AND_SWIFT_PACKAGE_MANAGER" + }, + "cocoapods": { + "required": false, + "status": "NOT_REQUIRED_FOR_THIS_RELEASE", + "distribution": "COCOAPODS_NOT_PUBLISHED" + }, + "appleDistribution": { + "required": false, + "status": "NOT_REQUIRED_FOR_THIS_RELEASE" + } + }, + "requiredCredentialsStatus": "GO" +} diff --git a/release/stable/v2.1.0/qualification-input.json b/release/stable/v2.1.0/qualification-input.json new file mode 100644 index 0000000..14dad08 --- /dev/null +++ b/release/stable/v2.1.0/qualification-input.json @@ -0,0 +1,269 @@ +{ + "schemaVersion": "scanly-release-qualification-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "gates": { + "software": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/software.json", + "sha256": "e8d507f8d5075a698f20604b4293a2db0f7cd86eb6c489ab4dfaae743325c1c5" + }, + "apiAbi": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/api-abi.json", + "sha256": "260cf19c4e9b73ef5b72d64246fa206cf91504e50c9a468dba3c03b698cbc7e7" + }, + "security": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/security.json", + "sha256": "16e712700e702c1b8cc7c3e6e7fc34069cc08da86a30de72c9528d58a9da692a" + }, + "sbom": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/sbom.cdx.json", + "sha256": "2b204af3904f6994eb4c69535cbdda4e79cb320f89dc09b798e87dfc0bd99697" + }, + "licenses": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/license-inventory.json", + "sha256": "929a90ce66b7605fdb09ddd6aabbc2780c8a9aeb97867df030517234d6fd00ff" + }, + "native": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/android-build-evidence.json", + "sha256": "eb59c7dca88456d929b6c7dd74b670183a94d9024a805c2461f0a9d04eeba991" + }, + "npmReproducibility": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/npm-reproducibility.json", + "sha256": "e23e014b2144e54be9742cdc359f97121b40a32790b20d563ecbfe73cc8ee357" + }, + "signing": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/signing.json", + "sha256": "fd8e68b7434883e8b88ecb7b337043a9776db439247c7a89d670285bc8b474e8" + }, + "publicationCredentials": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/publication-credentials-input.json", + "sha256": "5518f3e2970c33fe560996efc523dfb0df22df08773ca835a48274b6c492f123" + }, + "deployment": { + "status": "PASS", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "path": "release/stable/v2.1.0/evidence/deployment-input.json", + "sha256": "85fb809977b41f8e3484d642d13bfac116e2733c5bdb8fa62163863573aa03df" + } + }, + "signingEvidence": { + "scheme": "SSH_ED25519", + "githubAccount": "Yangjunjie-Lin", + "githubSigningKeyId": 1118906, + "publicKeyFingerprint": "SHA256:1odyhkHV3hKHFlE8QrELlBGMM/qlM5ftmiiVym/M6ck", + "publicKeyRegisteredAt": "2026-08-18T01:00:29.903+01:00", + "localSmokeTest": { + "status": "PASS", + "annotatedTagObjectCreated": true, + "sshSignatureBlockPresent": true, + "temporaryTagDeleted": true, + "tagObject": "60d17d680d457103be803e2e0f789320b1a00521" + } + }, + "npmPublicationEvidence": { + "registry": "https://registry.npmjs.org/", + "account": "yangjunjielin", + "organization": "scanly", + "organizationRole": "owner", + "trustedPublisherStatus": "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", + "trustedPublisherPackageCount": 10, + "trustedPublisherPackages": [ + "@scanly/parsers", + "@scanly/scenario-schema", + "@scanly/benchmark", + "@scanly/core", + "@scanly/engine-jsqr", + "@scanly/engine-zxing-js", + "@scanly/engine-zxing-cpp-wasm", + "@scanly/browser", + "@scanly/node", + "@scanly/react" + ], + "repository": "Yangjunjie-Lin/Scanly", + "workflowFile": "stable-npm-publish.yml", + "secretValueRecorded": false, + "provenanceWorkflow": ".github/workflows/stable-npm-publish.yml", + "provenanceMechanism": "GITHUB_ACTIONS_OIDC", + "qualificationRun": 34723767114, + "bootstrap": { + "package": "@scanly/url-safety", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "method": "INTERACTIVE_NPM_PROVENANCE_FILE", + "cliAuthenticatedAccount": "yangjunjielin", + "interactiveTwoFactorRequired": true, + "npmCredentialSharedWithCI": false, + "provenanceVerified": true, + "signedReleaseTagRequired": true, + "placeholderVersionAllowed": false, + "artifactPath": "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", + "provenancePath": "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "provenanceSha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87" + } + }, + "npmReproducibility": { + "schemaVersion": "scanly-v2.1-npm-reproducibility-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "status": "PASS", + "independentBuildJobs": [ + 103630562596, + 103630562477 + ], + "runId": 34722405931, + "cleanBuildRunA": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562596", + "cleanBuildRunB": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34722405931/job/103630562477", + "cleanBuildA": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "cleanBuildB": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "rawBuildAEqualsBuildB": true, + "detachedProvenanceVerified": [ + { + "package": "@scanly/benchmark", + "file": "release-dist/v2.1-build-A/provenance/scanly-benchmark-2.1.0.tgz.sigstore.json", + "sha256": "91f049bf254d70c161350aa7fba48975ad0e1ab0de3dc527067a8ef63052c4f5", + "artifactSha256": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "artifactSha512": "6e262c1f70df8cc03d30f8d096d7cf3fcfbaf392d5e9e7fea4ac3da4d3d098f95c9f064b6a75c738b49e984b4176b1e2b7a0d648e877bc6261962bcbec60d5cf", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/browser", + "file": "release-dist/v2.1-build-A/provenance/scanly-browser-2.1.0.tgz.sigstore.json", + "sha256": "1885ec28511f445404cdce916ae938c8a29e9bd8a00f2c3bacfced7c8eed4689", + "artifactSha256": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "artifactSha512": "07ffa207aa2a82609b0e808ae9221c3c773590406b3fe11dd45e4cf9840fc162170e42efda8d82ef72b39b9f33308e84db818a0239e563b37c66d008c768cf3e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/core", + "file": "release-dist/v2.1-build-A/provenance/scanly-core-2.1.0.tgz.sigstore.json", + "sha256": "f5ceaf7befb913824bd641c5b83955aee7904a428eee7e21e0f893c0fd121448", + "artifactSha256": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "artifactSha512": "c6a81231287f8c9b7a7e30481f65d497496771b003d7d3114a0bc7cdec03baac801baa6c095978b91d77f48ec489a03205508289cdfcdee9b72a5e8ead10e2bd", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-jsqr", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-jsqr-2.1.0.tgz.sigstore.json", + "sha256": "17195d688e9a98a74ec6de80f4c45bc0825676abdae75b3bf8e265f95d84ece6", + "artifactSha256": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "artifactSha512": "4550a7b56196e3f686de41cf278f9f28c9a9d57ccb6e6277a0ff707ecd8422c24d0ba53f63851d181b4b08b62026fc5d2b3191c522bc9c38d7745a0d58854d35", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-zxing-cpp-wasm", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-zxing-cpp-wasm-2.1.0.tgz.sigstore.json", + "sha256": "56ee3f1bc0c0457e1285f6603ce18f1742d35e21a86dbff29ae8055e16fd777e", + "artifactSha256": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "artifactSha512": "46734c49cc0776b26edc1fa6261f78a028a6dd0ec97a7433cd22101648836faeba04c547e90510372fa7da4133d0303f85118cec2b17100581a160cb047bbc39", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/engine-zxing-js", + "file": "release-dist/v2.1-build-A/provenance/scanly-engine-zxing-js-2.1.0.tgz.sigstore.json", + "sha256": "3759cf981d334bb26febb30219176162035e109e4c4d7f5fbaf84dcd59c9412d", + "artifactSha256": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "artifactSha512": "49a7c63ddf6317b9662f1be45d92b8ea9289fbb51d4d364a5c1e3e899e82101afd268d26a8af25cb8a739bd4d213f23ea499d4cb5b00e95fff6dc7cfcf0d0fda", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/node", + "file": "release-dist/v2.1-build-A/provenance/scanly-node-2.1.0.tgz.sigstore.json", + "sha256": "560113f3f22de7ade3073de69b9647b65ebefb963453fd1fec7fabff7e59259d", + "artifactSha256": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "artifactSha512": "88f404c159d400a4a03eff00294ad2076dc17dbc84b253c33fc8b742b975becc35d602e9fa529ff0ca91dc635148a8078d5564880973a63c49a2a7c6d1dc337e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/parsers", + "file": "release-dist/v2.1-build-A/provenance/scanly-parsers-2.1.0.tgz.sigstore.json", + "sha256": "e3d6ed1c243081c2b33e65ae2ec075493cb95b2153c050c3289ea62edd49401d", + "artifactSha256": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "artifactSha512": "1aa6833407e9d26ad02b160f253796e73d9517c5c9aa5d6d64795fccc37591f4d1997f443ff5ba34f0c05fea35c9bb217b23916f4b2418ef04f099a88b594755", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/react", + "file": "release-dist/v2.1-build-A/provenance/scanly-react-2.1.0.tgz.sigstore.json", + "sha256": "bc7af9441081c90a3b99725b3fd60c255869b27f26b6e35ae8a04ce167abfbaf", + "artifactSha256": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "artifactSha512": "8c2b8ee908aa097ce7c132a4fbd3671e1264665ed4cd34a7de3cc52188e800acbd2dd8d255cdb10f786b68638310d8301a11fb6c8c71b7e080bda88741f0a497", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/scenario-schema", + "file": "release-dist/v2.1-build-A/provenance/scanly-scenario-schema-2.1.0.tgz.sigstore.json", + "sha256": "4854cbcfad9121028cfd1f44afef9e20a12ccf9c28cc3c8c709d1a279ca123ab", + "artifactSha256": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "artifactSha512": "973fc3a387a978f8f52e63a82f574cf829c2bdd12bb5b0ad7f5766206e8610269d0e69aae90eb45f560f794a9223f9422010d54bc8a40e82ae32c027468eddbe", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + }, + { + "package": "@scanly/url-safety", + "file": "release-dist/v2.1-build-A/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "sha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "certificateIdentity": "https://github.com/Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@refs/pull/58/merge", + "verified": true + } + ], + "packageCount": 11, + "published": false + } +} diff --git a/release/stable/v2.1.0/release-policy.json b/release/stable/v2.1.0/release-policy.json new file mode 100644 index 0000000..4fe5445 --- /dev/null +++ b/release/stable/v2.1.0/release-policy.json @@ -0,0 +1,42 @@ +{ + "schemaVersion": "scanly-stable-release-policy-1", + "version": "2.1.0", + "featureFreeze": true, + "allowedChanges": [ + "RELEASE_BLOCKER", + "SECURITY_FIX", + "PACKAGING_FIX", + "SIGNING_FIX", + "REGISTRY_FIX", + "DOCUMENTATION_FIX", + "MANIFEST_FIX" + ], + "requiredGates": [ + "SOFTWARE_GO", + "MANIFEST_INTEGRITY_GO", + "API_ABI_GO", + "SECURITY_GO", + "SBOM_GO", + "LICENSE_GO", + "ARTIFACT_GO", + "REPRODUCIBILITY_GO", + "SIGNING_GO", + "REQUIRED_PUBLICATION_CREDENTIALS_GO" + ], + "physicalValidation": { + "requiredForPublication": false, + "status": "POST_RELEASE_VALIDATION_PENDING" + }, + "requiredPublicationChannels": [ + "github-release", + "npm", + "swift-package-manager", + "android-aar-github-release" + ], + "optionalPublicationChannels": [ + "cocoapods", + "maven-central", + "app-store", + "google-play" + ] +} diff --git a/release/stable/v2.1.0/reproducibility.json b/release/stable/v2.1.0/reproducibility.json new file mode 100644 index 0000000..6d82ce1 --- /dev/null +++ b/release/stable/v2.1.0/reproducibility.json @@ -0,0 +1,141 @@ +{ + "schemaVersion": "scanly-stable-reproducibility-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "status": "REPRODUCIBILITY_GO", + "checks": { + "npmTarballs": { + "status": "GO", + "cleanBuildA": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "cleanBuildB": { + "scanly-benchmark-2.1.0.tgz": "468b1cf37bf229029651fa1e6d6ec63414902578ea1f34b1f8632704ee47e9dd", + "scanly-browser-2.1.0.tgz": "c173f88dab2a0402ee549c9e609868c2b50145a8096ecc3c0c668a2823b92727", + "scanly-core-2.1.0.tgz": "7a529c1c940ace1d961bd1df668f668623bcbac1527827579433efffeb10e416", + "scanly-engine-jsqr-2.1.0.tgz": "b530dcb7949290f068fa3dd3805c0ee23b26d4554cc920f058f1c6c6f3afc68b", + "scanly-engine-zxing-cpp-wasm-2.1.0.tgz": "5215d8e09ff67ea07cececae51d34a975ae0396723ab235c627081568b1b796c", + "scanly-engine-zxing-js-2.1.0.tgz": "420c12ac6a7170c004cee7ce25af9e69f56165b1d50e31ebb1acf4eb03b2eecf", + "scanly-node-2.1.0.tgz": "13207d6afba9038cf721bb9462ff096aedaa658ed22596c10f5529eb08fc3573", + "scanly-parsers-2.1.0.tgz": "2d71b9713d1b8d084b8c9bdeb0ffbe079b3df54a16488e50dabe857c374cb868", + "scanly-react-2.1.0.tgz": "e709ec6f963a91f593a55efcda65e5cf524774154496a1011d8474bb7ab9b8ff", + "scanly-scenario-schema-2.1.0.tgz": "b55e951e1fcd5e129200beab60ee057dee23d4694409df75d007c0953a7e90b1", + "scanly-url-safety-2.1.0.tgz": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87" + }, + "rawBuildAEqualsBuildB": true, + "stableArtifactsCanonicalEquivalent": true, + "canonicalization": { + "schemaVersion": "scanly-npm-package-canonical-1", + "archiveParser": "NODE_GZIP_POSIX_TAR_VALIDATED", + "archiveMetadata": "EXCLUDED", + "entryOrder": "LEXICOGRAPHIC_UTF8_PATH", + "entryPathPrefix": "package/", + "textDetection": "STRICT_UTF8_WITHOUT_NUL", + "textLineEndings": "CRLF_OR_CR_TO_LF", + "framing": "PATH_NUL_DECIMAL_LENGTH_NUL_CONTENT_NUL" + } + }, + "androidAarNormalizedContents": { + "status": "GO", + "cleanBuildA": { + "runId": 34485084420, + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34485084420", + "conclusion": "SUCCESS", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "size": 1221160 + }, + "cleanBuildB": { + "runId": 34721927940, + "url": "https://github.com/Yangjunjie-Lin/Scanly/actions/runs/34721927940", + "conclusion": "SUCCESS", + "sha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b", + "canonicalContentSha256": "b862abeea8c37c19bf53040218b2f1a110c02ef3ad2a60348b469905bfe27212", + "size": 1221160 + }, + "rawBuildAEqualsBuildB": true, + "normalizedBuildAEqualsBuildB": true, + "canonicalization": { + "schemaVersion": "scanly-zip-content-canonical-1", + "archiveParser": "NODE_ZIP_CENTRAL_DIRECTORY_VALIDATED", + "archiveMetadata": "EXCLUDED", + "entryOrder": "LEXICOGRAPHIC_UTF8_PATH", + "nestedJarMetadata": "EXCLUDED_RECURSIVELY", + "fileContent": "EXACT_BYTES", + "framing": "PATH_NUL_DECIMAL_LENGTH_NUL_CONTENT_NUL" + }, + "selectedArtifactCanonicalEquivalent": true + }, + "iosSourcePackage": { + "status": "GO", + "normalizedSha256": "24d04f5d3a67c55aee95af7b4bf158f359a0c4b4da3f37b7df173269bd9af66f" + }, + "nativeCoreArtifacts": { + "status": "GO", + "sha256": "82dee124d9b88e8ee2c9ea6727a83a3a9e08fb6b70ca753c404928d70624d07a" + }, + "sbom": { + "status": "GO", + "cleanBuildAEqualsBuildB": true + }, + "manifest": { + "status": "GO", + "cleanBuildAEqualsBuildB": true, + "comparedFiles": [ + "artifact-manifest.json", + "sbom.cdx.json", + "license-inventory.json", + "physical-validation-status.json", + "release-policy.json", + "signing-manifest.json", + "publication-credentials.json", + "deployment.json", + "reproducibility.json", + "v2.1.0-manifest.json", + "v2.1.0-manifest.json.sha256", + "checksums.sha256", + "artifacts/ios/Package.swift", + "artifacts/native/scanly-core.h", + "qualification-input.json", + "artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "evidence/allowed-signers", + "evidence/api-abi.json", + "evidence/deployment-input.json", + "evidence/native-build-a.json", + "evidence/native-build-b.json", + "evidence/npm-build-run.json", + "evidence/npm-oidc-qualification.json", + "evidence/npm-reproducibility.json", + "evidence/publication-credentials-input.json", + "evidence/security.json", + "evidence/signing-probe.tag", + "evidence/signing.json", + "evidence/software.json", + "evidence/url-safety-benchmark.json", + "evidence/url-safety-tests.json", + "artifacts/npm/scanly-benchmark-2.1.0.tgz", + "artifacts/npm/scanly-browser-2.1.0.tgz", + "artifacts/npm/scanly-core-2.1.0.tgz", + "artifacts/npm/scanly-engine-jsqr-2.1.0.tgz", + "artifacts/npm/scanly-engine-zxing-cpp-wasm-2.1.0.tgz", + "artifacts/npm/scanly-engine-zxing-js-2.1.0.tgz", + "artifacts/npm/scanly-node-2.1.0.tgz", + "artifacts/npm/scanly-parsers-2.1.0.tgz", + "artifacts/npm/scanly-react-2.1.0.tgz", + "artifacts/npm/scanly-scenario-schema-2.1.0.tgz", + "artifacts/npm/scanly-url-safety-2.1.0.tgz" + ] + } + } +} diff --git a/release/stable/v2.1.0/sbom.cdx.json b/release/stable/v2.1.0/sbom.cdx.json new file mode 100644 index 0000000..387af37 --- /dev/null +++ b/release/stable/v2.1.0/sbom.cdx.json @@ -0,0 +1,5889 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:41d25665-2221-4000-8000-c3107505463b", + "version": 1, + "metadata": { + "timestamp": "2026-09-10T14:49:37+01:00", + "component": { + "type": "application", + "name": "scanly", + "version": "2.1.0" + }, + "properties": [ + { + "name": "scanly:sourceCommit", + "value": "6b017b53a4404f4feab776ac111b0ea9dacf2734" + }, + { + "name": "scanly:sourceTree", + "value": "c72430a2ab0a2d47e7cd095325088f1e053a5c41" + } + ] + }, + "components": [ + { + "type": "library", + "name": "@axe-core/playwright", + "version": "4.13.0", + "purl": "pkg:npm/%40axe-core/playwright@4.13.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "purl": "pkg:npm/%40babel/helper-string-parser@7.29.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "purl": "pkg:npm/%40babel/helper-validator-identifier@7.29.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@babel/parser", + "version": "7.29.8", + "purl": "pkg:npm/%40babel/parser@7.29.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@babel/types", + "version": "7.29.8", + "purl": "pkg:npm/%40babel/types@7.29.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "purl": "pkg:npm/%40bcoe/v8-coverage@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@emnapi/core", + "version": "1.10.0", + "purl": "pkg:npm/%40emnapi/core@1.10.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@emnapi/runtime", + "version": "1.10.0", + "purl": "pkg:npm/%40emnapi/runtime@1.10.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@emnapi/runtime", + "version": "1.11.3", + "purl": "pkg:npm/%40emnapi/runtime@1.11.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "purl": "pkg:npm/%40emnapi/wasi-threads@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/aix-ppc64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/aix-ppc64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/android-arm", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/android-arm@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/android-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/android-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/android-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/android-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/darwin-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/darwin-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/darwin-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/darwin-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/freebsd-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/freebsd-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/freebsd-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/freebsd-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-arm", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-arm@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-ia32", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-ia32@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-loong64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-loong64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-mips64el", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-mips64el@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-ppc64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-ppc64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-riscv64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-riscv64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-s390x", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-s390x@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/linux-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/linux-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/netbsd-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/netbsd-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/netbsd-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/netbsd-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/openbsd-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/openbsd-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/openbsd-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/openbsd-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/openharmony-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/openharmony-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/sunos-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/sunos-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/win32-arm64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/win32-arm64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/win32-ia32", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/win32-ia32@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@esbuild/win32-x64", + "version": "0.28.2", + "purl": "pkg:npm/%40esbuild/win32-x64@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@eslint-community/eslint-utils", + "version": "4.9.1", + "purl": "pkg:npm/%40eslint-community/eslint-utils@4.9.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@eslint-community/regexpp", + "version": "4.12.2", + "purl": "pkg:npm/%40eslint-community/regexpp@4.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@eslint/eslintrc", + "version": "2.1.4", + "purl": "pkg:npm/%40eslint/eslintrc@2.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@eslint/js", + "version": "8.57.0", + "purl": "pkg:npm/%40eslint/js@8.57.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@humanwhocodes/config-array", + "version": "0.11.14", + "purl": "pkg:npm/%40humanwhocodes/config-array@0.11.14", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@humanwhocodes/module-importer", + "version": "1.0.1", + "purl": "pkg:npm/%40humanwhocodes/module-importer@1.0.1", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@humanwhocodes/object-schema", + "version": "2.0.3", + "purl": "pkg:npm/%40humanwhocodes/object-schema@2.0.3", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "@img/colour", + "version": "1.1.0", + "purl": "pkg:npm/%40img/colour@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-darwin-arm64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-darwin-arm64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-darwin-x64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-darwin-x64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-freebsd-wasm32", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-freebsd-wasm32@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-darwin-arm64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-darwin-arm64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-darwin-x64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-darwin-x64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-arm", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-arm@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-arm64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-arm64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-ppc64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-ppc64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-riscv64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-riscv64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-s390x", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-s390x@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linux-x64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linux-x64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linuxmusl-arm64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linuxmusl-arm64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-libvips-linuxmusl-x64", + "version": "1.3.3", + "purl": "pkg:npm/%40img/sharp-libvips-linuxmusl-x64@1.3.3", + "licenses": [ + { + "expression": "LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-arm", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-arm@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-arm64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-arm64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-ppc64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-ppc64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-riscv64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-riscv64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-s390x", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-s390x@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linux-x64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linux-x64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linuxmusl-arm64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linuxmusl-arm64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-linuxmusl-x64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-linuxmusl-x64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-wasm32", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-wasm32@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0 AND LGPL-3.0-or-later AND MIT" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-webcontainers-wasm32", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-webcontainers-wasm32@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-win32-arm64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-win32-arm64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0 AND LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-win32-ia32", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-win32-ia32@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0 AND LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@img/sharp-win32-x64", + "version": "0.35.4", + "purl": "pkg:npm/%40img/sharp-win32-x64@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0 AND LGPL-3.0-or-later" + } + ] + }, + { + "type": "library", + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "purl": "pkg:npm/%40jridgewell/resolve-uri@3.1.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@jridgewell/sourcemap-codec", + "version": "1.6.0", + "purl": "pkg:npm/%40jridgewell/sourcemap-codec@1.6.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "purl": "pkg:npm/%40jridgewell/trace-mapping@0.3.31", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "purl": "pkg:npm/%40napi-rs/wasm-runtime@1.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/env", + "version": "15.5.25", + "purl": "pkg:npm/%40next/env@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/eslint-plugin-next", + "version": "15.5.25", + "purl": "pkg:npm/%40next/eslint-plugin-next@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-darwin-arm64", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-darwin-arm64@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-darwin-x64", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-darwin-x64@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-linux-arm64-gnu", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-linux-arm64-gnu@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-linux-arm64-musl", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-linux-arm64-musl@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-linux-x64-gnu", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-linux-x64-gnu@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-linux-x64-musl", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-linux-x64-musl@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-win32-arm64-msvc", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-win32-arm64-msvc@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@next/swc-win32-x64-msvc", + "version": "15.5.25", + "purl": "pkg:npm/%40next/swc-win32-x64-msvc@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@nodelib/fs.scandir", + "version": "2.1.5", + "purl": "pkg:npm/%40nodelib/fs.scandir@2.1.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@nodelib/fs.stat", + "version": "2.0.5", + "purl": "pkg:npm/%40nodelib/fs.stat@2.0.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@nodelib/fs.walk", + "version": "1.2.8", + "purl": "pkg:npm/%40nodelib/fs.walk@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@nolyfill/is-core-module", + "version": "1.0.39", + "purl": "pkg:npm/%40nolyfill/is-core-module@1.0.39", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@oxc-project/types", + "version": "0.149.0", + "purl": "pkg:npm/%40oxc-project/types@0.149.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@playwright/test", + "version": "1.63.0", + "purl": "pkg:npm/%40playwright/test@1.63.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-android-arm-eabi", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-android-arm-eabi@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-android-arm64", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-android-arm64@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-darwin-arm64@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-darwin-x64@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-freebsd-x64@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-arm-gnueabihf@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-arm64-gnu@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-arm64-musl@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-ppc64-gnu@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-s390x-gnu@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-x64-gnu@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-linux-x64-musl@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-openharmony-arm64@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-win32-arm64-msvc@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.8", + "purl": "pkg:npm/%40rolldown/binding-win32-x64-msvc@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "purl": "pkg:npm/%40rolldown/pluginutils@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rtsao/scc", + "version": "1.1.0", + "purl": "pkg:npm/%40rtsao/scc@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@rushstack/eslint-patch", + "version": "1.16.1", + "purl": "pkg:npm/%40rushstack/eslint-patch@1.16.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/benchmark", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/benchmark@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/browser", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/browser@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/core", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/core@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/engine-jsqr", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/engine-jsqr@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/engine-zxing-cpp-wasm", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/engine-zxing-cpp-wasm@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/engine-zxing-js", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/engine-zxing-js@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/node", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/node@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/parsers", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/parsers@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/react", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/react@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/scenario-schema", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/scenario-schema@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/url-safety", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/url-safety@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@scanly/web-demo", + "version": "2.1.0", + "purl": "pkg:npm/%40scanly/web-demo@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@standard-schema/spec", + "version": "1.1.0", + "purl": "pkg:npm/%40standard-schema/spec@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@swc/helpers", + "version": "0.5.15", + "purl": "pkg:npm/%40swc/helpers@0.5.15", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "@tybys/wasm-util", + "version": "0.10.3", + "purl": "pkg:npm/%40tybys/wasm-util@0.10.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/chai", + "version": "5.2.3", + "purl": "pkg:npm/%40types/chai@5.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/deep-eql", + "version": "4.0.2", + "purl": "pkg:npm/%40types/deep-eql@4.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/emscripten", + "version": "1.41.5", + "purl": "pkg:npm/%40types/emscripten@1.41.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/estree", + "version": "1.0.9", + "purl": "pkg:npm/%40types/estree@1.0.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/json5", + "version": "0.0.29", + "purl": "pkg:npm/%40types/json5@0.0.29", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/node", + "version": "20.19.43", + "purl": "pkg:npm/%40types/node@20.19.43", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/prop-types", + "version": "15.7.15", + "purl": "pkg:npm/%40types/prop-types@15.7.15", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/qrcode", + "version": "1.5.6", + "purl": "pkg:npm/%40types/qrcode@1.5.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/react-dom", + "version": "18.3.7", + "purl": "pkg:npm/%40types/react-dom@18.3.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@types/react", + "version": "18.3.28", + "purl": "pkg:npm/%40types/react@18.3.28", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/eslint-plugin", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/eslint-plugin@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/parser", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/parser@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/project-service", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/project-service@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/scope-manager", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/scope-manager@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/tsconfig-utils", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/tsconfig-utils@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/type-utils", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/type-utils@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/types", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/types@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/typescript-estree", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/typescript-estree@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/utils", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/utils@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@typescript-eslint/visitor-keys", + "version": "8.64.0", + "purl": "pkg:npm/%40typescript-eslint/visitor-keys@8.64.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@ungap/structured-clone", + "version": "1.3.2", + "purl": "pkg:npm/%40ungap/structured-clone@1.3.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-android-arm-eabi", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-android-arm-eabi@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-android-arm64", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-android-arm64@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-darwin-arm64", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-darwin-arm64@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-darwin-x64", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-darwin-x64@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-freebsd-x64", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-freebsd-x64@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-arm-gnueabihf", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm-gnueabihf@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-arm-musleabihf", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm-musleabihf@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-arm64-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm64-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-arm64-musl", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-arm64-musl@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-loong64-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-loong64-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-loong64-musl", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-loong64-musl@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-ppc64-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-ppc64-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-riscv64-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-riscv64-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-riscv64-musl", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-riscv64-musl@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-s390x-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-s390x-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-x64-gnu", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-x64-gnu@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-linux-x64-musl", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-linux-x64-musl@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-openharmony-arm64", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-openharmony-arm64@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-wasm32-wasi", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-wasm32-wasi@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-win32-arm64-msvc", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-arm64-msvc@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-win32-ia32-msvc", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-ia32-msvc@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@unrs/resolver-binding-win32-x64-msvc", + "version": "1.12.2", + "purl": "pkg:npm/%40unrs/resolver-binding-win32-x64-msvc@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/coverage-v8", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/coverage-v8@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/expect", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/expect@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/mocker", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/mocker@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/pretty-format", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/pretty-format@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/runner", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/runner@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/snapshot", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/snapshot@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/spy", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/spy@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@vitest/utils", + "version": "4.1.11", + "purl": "pkg:npm/%40vitest/utils@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@zxing/library", + "version": "0.21.3", + "purl": "pkg:npm/%40zxing/library@0.21.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "@zxing/text-encoding", + "version": "0.9.0", + "purl": "pkg:npm/%40zxing/text-encoding@0.9.0", + "licenses": [ + { + "expression": "(Unlicense OR Apache-2.0)" + } + ] + }, + { + "type": "library", + "name": "acorn-jsx", + "version": "5.3.2", + "purl": "pkg:npm/acorn-jsx@5.3.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "acorn", + "version": "8.17.0", + "purl": "pkg:npm/acorn@8.17.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ajv-formats", + "version": "3.0.1", + "purl": "pkg:npm/ajv-formats@3.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ajv", + "version": "6.15.0", + "purl": "pkg:npm/ajv@6.15.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ajv", + "version": "8.20.0", + "purl": "pkg:npm/ajv@8.20.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ansi-regex", + "version": "5.0.1", + "purl": "pkg:npm/ansi-regex@5.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ansi-styles", + "version": "4.3.0", + "purl": "pkg:npm/ansi-styles@4.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "argparse", + "version": "2.0.1", + "purl": "pkg:npm/argparse@2.0.1", + "licenses": [ + { + "expression": "Python-2.0" + } + ] + }, + { + "type": "library", + "name": "aria-query", + "version": "5.3.2", + "purl": "pkg:npm/aria-query@5.3.2", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "array-buffer-byte-length", + "version": "1.0.2", + "purl": "pkg:npm/array-buffer-byte-length@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array-includes", + "version": "3.1.9", + "purl": "pkg:npm/array-includes@3.1.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array.prototype.findlast", + "version": "1.2.5", + "purl": "pkg:npm/array.prototype.findlast@1.2.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array.prototype.findlastindex", + "version": "1.2.6", + "purl": "pkg:npm/array.prototype.findlastindex@1.2.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array.prototype.flat", + "version": "1.3.3", + "purl": "pkg:npm/array.prototype.flat@1.3.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array.prototype.flatmap", + "version": "1.3.3", + "purl": "pkg:npm/array.prototype.flatmap@1.3.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "array.prototype.tosorted", + "version": "1.1.4", + "purl": "pkg:npm/array.prototype.tosorted@1.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "arraybuffer.prototype.slice", + "version": "1.0.4", + "purl": "pkg:npm/arraybuffer.prototype.slice@1.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "assertion-error", + "version": "2.0.1", + "purl": "pkg:npm/assertion-error@2.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ast-types-flow", + "version": "0.0.8", + "purl": "pkg:npm/ast-types-flow@0.0.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ast-v8-to-istanbul", + "version": "1.0.6", + "purl": "pkg:npm/ast-v8-to-istanbul@1.0.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "async-function", + "version": "1.0.0", + "purl": "pkg:npm/async-function@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "available-typed-arrays", + "version": "1.0.7", + "purl": "pkg:npm/available-typed-arrays@1.0.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "axe-core", + "version": "4.13.0", + "purl": "pkg:npm/axe-core@4.13.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "axobject-query", + "version": "4.1.0", + "purl": "pkg:npm/axobject-query@4.1.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "balanced-match", + "version": "1.0.2", + "purl": "pkg:npm/balanced-match@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "balanced-match", + "version": "4.0.4", + "purl": "pkg:npm/balanced-match@4.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "brace-expansion", + "version": "1.1.18", + "purl": "pkg:npm/brace-expansion@1.1.18", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "brace-expansion", + "version": "5.0.9", + "purl": "pkg:npm/brace-expansion@5.0.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "braces", + "version": "3.0.3", + "purl": "pkg:npm/braces@3.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "purl": "pkg:npm/call-bind-apply-helpers@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "call-bind", + "version": "1.0.9", + "purl": "pkg:npm/call-bind@1.0.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "call-bound", + "version": "1.0.4", + "purl": "pkg:npm/call-bound@1.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "callsites", + "version": "3.1.0", + "purl": "pkg:npm/callsites@3.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "camelcase", + "version": "5.3.1", + "purl": "pkg:npm/camelcase@5.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "caniuse-lite", + "version": "1.0.30001769", + "purl": "pkg:npm/caniuse-lite@1.0.30001769", + "licenses": [ + { + "expression": "CC-BY-4.0" + } + ] + }, + { + "type": "library", + "name": "chai", + "version": "6.2.2", + "purl": "pkg:npm/chai@6.2.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "chalk", + "version": "4.1.2", + "purl": "pkg:npm/chalk@4.1.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "client-only", + "version": "0.0.1", + "purl": "pkg:npm/client-only@0.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "cliui", + "version": "6.0.0", + "purl": "pkg:npm/cliui@6.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "color-convert", + "version": "2.0.1", + "purl": "pkg:npm/color-convert@2.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "color-name", + "version": "1.1.4", + "purl": "pkg:npm/color-name@1.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "concat-map", + "version": "0.0.1", + "purl": "pkg:npm/concat-map@0.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "convert-source-map", + "version": "2.0.0", + "purl": "pkg:npm/convert-source-map@2.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "cross-spawn", + "version": "7.0.6", + "purl": "pkg:npm/cross-spawn@7.0.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "csstype", + "version": "3.2.3", + "purl": "pkg:npm/csstype@3.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "damerau-levenshtein", + "version": "1.0.8", + "purl": "pkg:npm/damerau-levenshtein@1.0.8", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "data-view-buffer", + "version": "1.0.2", + "purl": "pkg:npm/data-view-buffer@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "data-view-byte-length", + "version": "1.0.2", + "purl": "pkg:npm/data-view-byte-length@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "data-view-byte-offset", + "version": "1.0.1", + "purl": "pkg:npm/data-view-byte-offset@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "debug", + "version": "3.2.7", + "purl": "pkg:npm/debug@3.2.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "debug", + "version": "4.4.3", + "purl": "pkg:npm/debug@4.4.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "decamelize", + "version": "1.2.0", + "purl": "pkg:npm/decamelize@1.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "deep-is", + "version": "0.1.4", + "purl": "pkg:npm/deep-is@0.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "define-data-property", + "version": "1.1.4", + "purl": "pkg:npm/define-data-property@1.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "define-properties", + "version": "1.2.1", + "purl": "pkg:npm/define-properties@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "detect-libc", + "version": "2.1.2", + "purl": "pkg:npm/detect-libc@2.1.2", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "dijkstrajs", + "version": "1.0.3", + "purl": "pkg:npm/dijkstrajs@1.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "doctrine", + "version": "2.1.0", + "purl": "pkg:npm/doctrine@2.1.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "doctrine", + "version": "3.0.0", + "purl": "pkg:npm/doctrine@3.0.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "dunder-proto", + "version": "1.0.1", + "purl": "pkg:npm/dunder-proto@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "emoji-regex", + "version": "8.0.0", + "purl": "pkg:npm/emoji-regex@8.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "emoji-regex", + "version": "9.2.2", + "purl": "pkg:npm/emoji-regex@9.2.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "entities", + "version": "6.0.1", + "purl": "pkg:npm/entities@6.0.1", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "es-abstract-get", + "version": "1.0.0", + "purl": "pkg:npm/es-abstract-get@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-abstract", + "version": "1.24.2", + "purl": "pkg:npm/es-abstract@1.24.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-define-property", + "version": "1.0.1", + "purl": "pkg:npm/es-define-property@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-errors", + "version": "1.3.0", + "purl": "pkg:npm/es-errors@1.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-iterator-helpers", + "version": "1.3.3", + "purl": "pkg:npm/es-iterator-helpers@1.3.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-module-lexer", + "version": "2.3.2", + "purl": "pkg:npm/es-module-lexer@2.3.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-object-atoms", + "version": "1.1.2", + "purl": "pkg:npm/es-object-atoms@1.1.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-set-tostringtag", + "version": "2.1.0", + "purl": "pkg:npm/es-set-tostringtag@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-shim-unscopables", + "version": "1.1.0", + "purl": "pkg:npm/es-shim-unscopables@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "es-to-primitive", + "version": "1.3.1", + "purl": "pkg:npm/es-to-primitive@1.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "esbuild", + "version": "0.28.2", + "purl": "pkg:npm/esbuild@0.28.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "escape-string-regexp", + "version": "4.0.0", + "purl": "pkg:npm/escape-string-regexp@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-config-next", + "version": "15.5.25", + "purl": "pkg:npm/eslint-config-next@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-import-resolver-node", + "version": "0.3.10", + "purl": "pkg:npm/eslint-import-resolver-node@0.3.10", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-import-resolver-typescript", + "version": "3.10.1", + "purl": "pkg:npm/eslint-import-resolver-typescript@3.10.1", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "eslint-module-utils", + "version": "2.13.0", + "purl": "pkg:npm/eslint-module-utils@2.13.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-plugin-import", + "version": "2.32.0", + "purl": "pkg:npm/eslint-plugin-import@2.32.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-plugin-jsx-a11y", + "version": "6.10.2", + "purl": "pkg:npm/eslint-plugin-jsx-a11y@6.10.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-plugin-react-hooks", + "version": "5.2.0", + "purl": "pkg:npm/eslint-plugin-react-hooks@5.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-plugin-react", + "version": "7.37.5", + "purl": "pkg:npm/eslint-plugin-react@7.37.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "eslint-scope", + "version": "7.2.2", + "purl": "pkg:npm/eslint-scope@7.2.2", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "eslint-visitor-keys", + "version": "3.4.3", + "purl": "pkg:npm/eslint-visitor-keys@3.4.3", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "eslint-visitor-keys", + "version": "5.0.1", + "purl": "pkg:npm/eslint-visitor-keys@5.0.1", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "eslint", + "version": "8.57.0", + "purl": "pkg:npm/eslint@8.57.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "espree", + "version": "9.6.1", + "purl": "pkg:npm/espree@9.6.1", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "esquery", + "version": "1.7.0", + "purl": "pkg:npm/esquery@1.7.0", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "esrecurse", + "version": "4.3.0", + "purl": "pkg:npm/esrecurse@4.3.0", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "estraverse", + "version": "5.3.0", + "purl": "pkg:npm/estraverse@5.3.0", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "estree-walker", + "version": "3.0.3", + "purl": "pkg:npm/estree-walker@3.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "esutils", + "version": "2.0.3", + "purl": "pkg:npm/esutils@2.0.3", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "expect-type", + "version": "1.4.0", + "purl": "pkg:npm/expect-type@1.4.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "fast-deep-equal", + "version": "3.1.3", + "purl": "pkg:npm/fast-deep-equal@3.1.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fast-glob", + "version": "3.3.1", + "purl": "pkg:npm/fast-glob@3.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fast-json-stable-stringify", + "version": "2.1.0", + "purl": "pkg:npm/fast-json-stable-stringify@2.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fast-levenshtein", + "version": "2.0.6", + "purl": "pkg:npm/fast-levenshtein@2.0.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fast-uri", + "version": "3.1.6", + "purl": "pkg:npm/fast-uri@3.1.6", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "fastq", + "version": "1.20.1", + "purl": "pkg:npm/fastq@1.20.1", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "fdir", + "version": "6.5.0", + "purl": "pkg:npm/fdir@6.5.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "file-entry-cache", + "version": "6.0.1", + "purl": "pkg:npm/file-entry-cache@6.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fill-range", + "version": "7.1.1", + "purl": "pkg:npm/fill-range@7.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "find-up", + "version": "4.1.0", + "purl": "pkg:npm/find-up@4.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "find-up", + "version": "5.0.0", + "purl": "pkg:npm/find-up@5.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "flat-cache", + "version": "3.2.0", + "purl": "pkg:npm/flat-cache@3.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "flatted", + "version": "3.4.2", + "purl": "pkg:npm/flatted@3.4.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "for-each", + "version": "0.3.5", + "purl": "pkg:npm/for-each@0.3.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "fs.realpath", + "version": "1.0.0", + "purl": "pkg:npm/fs.realpath@1.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "fsevents", + "version": "2.3.3", + "purl": "pkg:npm/fsevents@2.3.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "function-bind", + "version": "1.1.2", + "purl": "pkg:npm/function-bind@1.1.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "function.prototype.name", + "version": "1.2.0", + "purl": "pkg:npm/function.prototype.name@1.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "functions-have-names", + "version": "1.2.3", + "purl": "pkg:npm/functions-have-names@1.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "generator-function", + "version": "2.0.1", + "purl": "pkg:npm/generator-function@2.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "get-caller-file", + "version": "2.0.5", + "purl": "pkg:npm/get-caller-file@2.0.5", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "get-intrinsic", + "version": "1.3.0", + "purl": "pkg:npm/get-intrinsic@1.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "get-proto", + "version": "1.0.1", + "purl": "pkg:npm/get-proto@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "get-symbol-description", + "version": "1.1.0", + "purl": "pkg:npm/get-symbol-description@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "get-tsconfig", + "version": "4.14.0", + "purl": "pkg:npm/get-tsconfig@4.14.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "glob-parent", + "version": "5.1.2", + "purl": "pkg:npm/glob-parent@5.1.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "glob-parent", + "version": "6.0.2", + "purl": "pkg:npm/glob-parent@6.0.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "glob", + "version": "7.2.3", + "purl": "pkg:npm/glob@7.2.3", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "globals", + "version": "13.24.0", + "purl": "pkg:npm/globals@13.24.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "globalthis", + "version": "1.0.4", + "purl": "pkg:npm/globalthis@1.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "gopd", + "version": "1.2.0", + "purl": "pkg:npm/gopd@1.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "graphemer", + "version": "1.4.0", + "purl": "pkg:npm/graphemer@1.4.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-bigints", + "version": "1.1.0", + "purl": "pkg:npm/has-bigints@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-flag", + "version": "4.0.0", + "purl": "pkg:npm/has-flag@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-property-descriptors", + "version": "1.0.2", + "purl": "pkg:npm/has-property-descriptors@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-proto", + "version": "1.2.0", + "purl": "pkg:npm/has-proto@1.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-symbols", + "version": "1.1.0", + "purl": "pkg:npm/has-symbols@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "has-tostringtag", + "version": "1.0.2", + "purl": "pkg:npm/has-tostringtag@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "hasown", + "version": "2.0.4", + "purl": "pkg:npm/hasown@2.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "html-escaper", + "version": "2.0.2", + "purl": "pkg:npm/html-escaper@2.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ignore", + "version": "5.3.2", + "purl": "pkg:npm/ignore@5.3.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ignore", + "version": "7.0.6", + "purl": "pkg:npm/ignore@7.0.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "import-fresh", + "version": "3.3.1", + "purl": "pkg:npm/import-fresh@3.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "imurmurhash", + "version": "0.1.4", + "purl": "pkg:npm/imurmurhash@0.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "inflight", + "version": "1.0.6", + "purl": "pkg:npm/inflight@1.0.6", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "inherits", + "version": "2.0.4", + "purl": "pkg:npm/inherits@2.0.4", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "internal-slot", + "version": "1.1.0", + "purl": "pkg:npm/internal-slot@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ipaddr.js", + "version": "2.2.0", + "purl": "pkg:npm/ipaddr.js@2.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-array-buffer", + "version": "3.0.5", + "purl": "pkg:npm/is-array-buffer@3.0.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-async-function", + "version": "2.1.1", + "purl": "pkg:npm/is-async-function@2.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-bigint", + "version": "1.1.0", + "purl": "pkg:npm/is-bigint@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-boolean-object", + "version": "1.2.2", + "purl": "pkg:npm/is-boolean-object@1.2.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-bun-module", + "version": "2.0.0", + "purl": "pkg:npm/is-bun-module@2.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-callable", + "version": "1.2.7", + "purl": "pkg:npm/is-callable@1.2.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-core-module", + "version": "2.16.2", + "purl": "pkg:npm/is-core-module@2.16.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-data-view", + "version": "1.0.2", + "purl": "pkg:npm/is-data-view@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-date-object", + "version": "1.1.0", + "purl": "pkg:npm/is-date-object@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-document.all", + "version": "1.0.0", + "purl": "pkg:npm/is-document.all@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-extglob", + "version": "2.1.1", + "purl": "pkg:npm/is-extglob@2.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-finalizationregistry", + "version": "1.1.1", + "purl": "pkg:npm/is-finalizationregistry@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-fullwidth-code-point", + "version": "3.0.0", + "purl": "pkg:npm/is-fullwidth-code-point@3.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-generator-function", + "version": "1.1.2", + "purl": "pkg:npm/is-generator-function@1.1.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-glob", + "version": "4.0.3", + "purl": "pkg:npm/is-glob@4.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-map", + "version": "2.0.3", + "purl": "pkg:npm/is-map@2.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-negative-zero", + "version": "2.0.3", + "purl": "pkg:npm/is-negative-zero@2.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-number-object", + "version": "1.1.1", + "purl": "pkg:npm/is-number-object@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-number", + "version": "7.0.0", + "purl": "pkg:npm/is-number@7.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-path-inside", + "version": "3.0.3", + "purl": "pkg:npm/is-path-inside@3.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-regex", + "version": "1.2.1", + "purl": "pkg:npm/is-regex@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-set", + "version": "2.0.3", + "purl": "pkg:npm/is-set@2.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-shared-array-buffer", + "version": "1.0.4", + "purl": "pkg:npm/is-shared-array-buffer@1.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-string", + "version": "1.1.1", + "purl": "pkg:npm/is-string@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-symbol", + "version": "1.1.1", + "purl": "pkg:npm/is-symbol@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-typed-array", + "version": "1.1.15", + "purl": "pkg:npm/is-typed-array@1.1.15", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-weakmap", + "version": "2.0.2", + "purl": "pkg:npm/is-weakmap@2.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-weakref", + "version": "1.1.1", + "purl": "pkg:npm/is-weakref@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "is-weakset", + "version": "2.0.4", + "purl": "pkg:npm/is-weakset@2.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "isarray", + "version": "2.0.5", + "purl": "pkg:npm/isarray@2.0.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "isexe", + "version": "2.0.0", + "purl": "pkg:npm/isexe@2.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "purl": "pkg:npm/istanbul-lib-coverage@3.2.2", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "istanbul-lib-report", + "version": "3.0.1", + "purl": "pkg:npm/istanbul-lib-report@3.0.1", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "istanbul-reports", + "version": "3.2.0", + "purl": "pkg:npm/istanbul-reports@3.2.0", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "iterator.prototype", + "version": "1.1.5", + "purl": "pkg:npm/iterator.prototype@1.1.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "js-tokens", + "version": "10.0.0", + "purl": "pkg:npm/js-tokens@10.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "js-tokens", + "version": "4.0.0", + "purl": "pkg:npm/js-tokens@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "js-yaml", + "version": "4.3.2", + "purl": "pkg:npm/js-yaml@4.3.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "js-yaml", + "version": "5.4.1", + "purl": "pkg:npm/js-yaml@5.4.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "json-buffer", + "version": "3.0.1", + "purl": "pkg:npm/json-buffer@3.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "json-schema-traverse", + "version": "0.4.1", + "purl": "pkg:npm/json-schema-traverse@0.4.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "json-schema-traverse", + "version": "1.0.0", + "purl": "pkg:npm/json-schema-traverse@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "json-stable-stringify-without-jsonify", + "version": "1.0.1", + "purl": "pkg:npm/json-stable-stringify-without-jsonify@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "json5", + "version": "1.0.2", + "purl": "pkg:npm/json5@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "jsqr", + "version": "1.4.0", + "purl": "pkg:npm/jsqr@1.4.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "jsx-ast-utils", + "version": "3.3.5", + "purl": "pkg:npm/jsx-ast-utils@3.3.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "keyv", + "version": "4.5.4", + "purl": "pkg:npm/keyv@4.5.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "language-subtag-registry", + "version": "0.3.23", + "purl": "pkg:npm/language-subtag-registry@0.3.23", + "licenses": [ + { + "expression": "CC0-1.0" + } + ] + }, + { + "type": "library", + "name": "language-tags", + "version": "1.0.9", + "purl": "pkg:npm/language-tags@1.0.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "levn", + "version": "0.4.1", + "purl": "pkg:npm/levn@0.4.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-android-arm64@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-darwin-arm64@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-darwin-x64@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-freebsd-x64@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-linux-arm-gnueabihf@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-linux-arm64-gnu@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-linux-arm64-musl@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-linux-x64-gnu@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-linux-x64-musl@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-win32-arm64-msvc@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss-win32-x64-msvc@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "lightningcss", + "version": "1.33.0", + "purl": "pkg:npm/lightningcss@1.33.0", + "licenses": [ + { + "expression": "MPL-2.0" + } + ] + }, + { + "type": "library", + "name": "locate-path", + "version": "5.0.0", + "purl": "pkg:npm/locate-path@5.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "locate-path", + "version": "6.0.0", + "purl": "pkg:npm/locate-path@6.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "lodash.merge", + "version": "4.6.2", + "purl": "pkg:npm/lodash.merge@4.6.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "loose-envify", + "version": "1.4.0", + "purl": "pkg:npm/loose-envify@1.4.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "magic-string", + "version": "0.30.21", + "purl": "pkg:npm/magic-string@0.30.21", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "magicast", + "version": "0.5.4", + "purl": "pkg:npm/magicast@0.5.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "make-dir", + "version": "4.0.0", + "purl": "pkg:npm/make-dir@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "math-intrinsics", + "version": "1.1.0", + "purl": "pkg:npm/math-intrinsics@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "merge2", + "version": "1.4.1", + "purl": "pkg:npm/merge2@1.4.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "micromatch", + "version": "4.0.8", + "purl": "pkg:npm/micromatch@4.0.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "minimatch", + "version": "10.2.5", + "purl": "pkg:npm/minimatch@10.2.5", + "licenses": [ + { + "expression": "BlueOak-1.0.0" + } + ] + }, + { + "type": "library", + "name": "minimatch", + "version": "3.1.5", + "purl": "pkg:npm/minimatch@3.1.5", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "minimist", + "version": "1.2.8", + "purl": "pkg:npm/minimist@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ms", + "version": "2.1.3", + "purl": "pkg:npm/ms@2.1.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "nanoid", + "version": "3.3.18", + "purl": "pkg:npm/nanoid@3.3.18", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "napi-postinstall", + "version": "0.3.4", + "purl": "pkg:npm/napi-postinstall@0.3.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "natural-compare", + "version": "1.4.0", + "purl": "pkg:npm/natural-compare@1.4.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "next", + "version": "15.5.25", + "purl": "pkg:npm/next@15.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "node-exports-info", + "version": "1.6.2", + "purl": "pkg:npm/node-exports-info@1.6.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object-assign", + "version": "4.1.1", + "purl": "pkg:npm/object-assign@4.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object-inspect", + "version": "1.13.4", + "purl": "pkg:npm/object-inspect@1.13.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object-keys", + "version": "1.1.1", + "purl": "pkg:npm/object-keys@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object.assign", + "version": "4.1.7", + "purl": "pkg:npm/object.assign@4.1.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object.entries", + "version": "1.1.9", + "purl": "pkg:npm/object.entries@1.1.9", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object.fromentries", + "version": "2.0.8", + "purl": "pkg:npm/object.fromentries@2.0.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object.groupby", + "version": "1.0.3", + "purl": "pkg:npm/object.groupby@1.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "object.values", + "version": "1.2.1", + "purl": "pkg:npm/object.values@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "obug", + "version": "2.2.1", + "purl": "pkg:npm/obug@2.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "once", + "version": "1.4.0", + "purl": "pkg:npm/once@1.4.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "optionator", + "version": "0.9.4", + "purl": "pkg:npm/optionator@0.9.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "own-keys", + "version": "1.0.1", + "purl": "pkg:npm/own-keys@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "p-limit", + "version": "2.3.0", + "purl": "pkg:npm/p-limit@2.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "p-limit", + "version": "3.1.0", + "purl": "pkg:npm/p-limit@3.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "p-locate", + "version": "4.1.0", + "purl": "pkg:npm/p-locate@4.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "p-locate", + "version": "5.0.0", + "purl": "pkg:npm/p-locate@5.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "p-try", + "version": "2.2.0", + "purl": "pkg:npm/p-try@2.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "parent-module", + "version": "1.0.1", + "purl": "pkg:npm/parent-module@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "parse5", + "version": "7.3.0", + "purl": "pkg:npm/parse5@7.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "path-exists", + "version": "4.0.0", + "purl": "pkg:npm/path-exists@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "path-is-absolute", + "version": "1.0.1", + "purl": "pkg:npm/path-is-absolute@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "path-key", + "version": "3.1.1", + "purl": "pkg:npm/path-key@3.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "path-parse", + "version": "1.0.7", + "purl": "pkg:npm/path-parse@1.0.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "pathe", + "version": "2.0.3", + "purl": "pkg:npm/pathe@2.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "picocolors", + "version": "1.1.1", + "purl": "pkg:npm/picocolors@1.1.1", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "picomatch", + "version": "2.3.2", + "purl": "pkg:npm/picomatch@2.3.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "picomatch", + "version": "4.0.4", + "purl": "pkg:npm/picomatch@4.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "picomatch", + "version": "4.0.7", + "purl": "pkg:npm/picomatch@4.0.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "playwright-core", + "version": "1.63.0", + "purl": "pkg:npm/playwright-core@1.63.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "playwright", + "version": "1.63.0", + "purl": "pkg:npm/playwright@1.63.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "pngjs", + "version": "5.0.0", + "purl": "pkg:npm/pngjs@5.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "possible-typed-array-names", + "version": "1.1.0", + "purl": "pkg:npm/possible-typed-array-names@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "postcss", + "version": "8.5.25", + "purl": "pkg:npm/postcss@8.5.25", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "prelude-ls", + "version": "1.2.1", + "purl": "pkg:npm/prelude-ls@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "prop-types", + "version": "15.8.1", + "purl": "pkg:npm/prop-types@15.8.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "punycode", + "version": "2.3.1", + "purl": "pkg:npm/punycode@2.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "qrcode", + "version": "1.5.4", + "purl": "pkg:npm/qrcode@1.5.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "queue-microtask", + "version": "1.2.3", + "purl": "pkg:npm/queue-microtask@1.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "react-dom", + "version": "18.3.1", + "purl": "pkg:npm/react-dom@18.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "react-is", + "version": "16.13.1", + "purl": "pkg:npm/react-is@16.13.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "react", + "version": "18.3.1", + "purl": "pkg:npm/react@18.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "reflect.getprototypeof", + "version": "1.0.10", + "purl": "pkg:npm/reflect.getprototypeof@1.0.10", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "regexp.prototype.flags", + "version": "1.5.4", + "purl": "pkg:npm/regexp.prototype.flags@1.5.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "require-directory", + "version": "2.1.1", + "purl": "pkg:npm/require-directory@2.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "require-from-string", + "version": "2.0.2", + "purl": "pkg:npm/require-from-string@2.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "require-main-filename", + "version": "2.0.0", + "purl": "pkg:npm/require-main-filename@2.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "resolve-from", + "version": "4.0.0", + "purl": "pkg:npm/resolve-from@4.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "resolve-pkg-maps", + "version": "1.0.0", + "purl": "pkg:npm/resolve-pkg-maps@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "resolve", + "version": "2.0.0-next.7", + "purl": "pkg:npm/resolve@2.0.0-next.7", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "reusify", + "version": "1.1.0", + "purl": "pkg:npm/reusify@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "rimraf", + "version": "3.0.2", + "purl": "pkg:npm/rimraf@3.0.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "rolldown", + "version": "1.2.8", + "purl": "pkg:npm/rolldown@1.2.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "run-parallel", + "version": "1.2.0", + "purl": "pkg:npm/run-parallel@1.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "safe-array-concat", + "version": "1.1.4", + "purl": "pkg:npm/safe-array-concat@1.1.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "safe-push-apply", + "version": "1.0.0", + "purl": "pkg:npm/safe-push-apply@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "safe-regex-test", + "version": "1.1.0", + "purl": "pkg:npm/safe-regex-test@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "scheduler", + "version": "0.23.2", + "purl": "pkg:npm/scheduler@0.23.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "semver", + "version": "6.3.1", + "purl": "pkg:npm/semver@6.3.1", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "semver", + "version": "7.8.5", + "purl": "pkg:npm/semver@7.8.5", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "set-blocking", + "version": "2.0.0", + "purl": "pkg:npm/set-blocking@2.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "set-function-length", + "version": "1.2.2", + "purl": "pkg:npm/set-function-length@1.2.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "set-function-name", + "version": "2.0.2", + "purl": "pkg:npm/set-function-name@2.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "set-proto", + "version": "1.0.0", + "purl": "pkg:npm/set-proto@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "sharp", + "version": "0.35.4", + "purl": "pkg:npm/sharp@0.35.4", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "shebang-command", + "version": "2.0.0", + "purl": "pkg:npm/shebang-command@2.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "shebang-regex", + "version": "3.0.0", + "purl": "pkg:npm/shebang-regex@3.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "side-channel-list", + "version": "1.0.1", + "purl": "pkg:npm/side-channel-list@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "side-channel-map", + "version": "1.0.1", + "purl": "pkg:npm/side-channel-map@1.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "side-channel-weakmap", + "version": "1.0.2", + "purl": "pkg:npm/side-channel-weakmap@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "side-channel", + "version": "1.1.1", + "purl": "pkg:npm/side-channel@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "siginfo", + "version": "2.0.0", + "purl": "pkg:npm/siginfo@2.0.0", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "source-map-js", + "version": "1.2.1", + "purl": "pkg:npm/source-map-js@1.2.1", + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ] + }, + { + "type": "library", + "name": "stable-hash", + "version": "0.0.5", + "purl": "pkg:npm/stable-hash@0.0.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "stackback", + "version": "0.0.2", + "purl": "pkg:npm/stackback@0.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "std-env", + "version": "4.2.0", + "purl": "pkg:npm/std-env@4.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "stop-iteration-iterator", + "version": "1.1.0", + "purl": "pkg:npm/stop-iteration-iterator@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string-width", + "version": "4.2.3", + "purl": "pkg:npm/string-width@4.2.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.includes", + "version": "2.0.1", + "purl": "pkg:npm/string.prototype.includes@2.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.matchall", + "version": "4.0.12", + "purl": "pkg:npm/string.prototype.matchall@4.0.12", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.repeat", + "version": "1.0.0", + "purl": "pkg:npm/string.prototype.repeat@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.trim", + "version": "1.2.11", + "purl": "pkg:npm/string.prototype.trim@1.2.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.trimend", + "version": "1.0.10", + "purl": "pkg:npm/string.prototype.trimend@1.0.10", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "string.prototype.trimstart", + "version": "1.0.8", + "purl": "pkg:npm/string.prototype.trimstart@1.0.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "strip-ansi", + "version": "6.0.1", + "purl": "pkg:npm/strip-ansi@6.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "strip-bom", + "version": "3.0.0", + "purl": "pkg:npm/strip-bom@3.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "strip-json-comments", + "version": "3.1.1", + "purl": "pkg:npm/strip-json-comments@3.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "styled-jsx", + "version": "5.1.6", + "purl": "pkg:npm/styled-jsx@5.1.6", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "supports-color", + "version": "7.2.0", + "purl": "pkg:npm/supports-color@7.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "purl": "pkg:npm/supports-preserve-symlinks-flag@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tagged-tag", + "version": "1.0.0", + "purl": "pkg:npm/tagged-tag@1.0.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "text-table", + "version": "0.2.0", + "purl": "pkg:npm/text-table@0.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tinybench", + "version": "2.9.0", + "purl": "pkg:npm/tinybench@2.9.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tinyexec", + "version": "1.3.1", + "purl": "pkg:npm/tinyexec@1.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tinyglobby", + "version": "0.2.17", + "purl": "pkg:npm/tinyglobby@0.2.17", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tinyrainbow", + "version": "3.1.1", + "purl": "pkg:npm/tinyrainbow@3.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "to-regex-range", + "version": "5.0.1", + "purl": "pkg:npm/to-regex-range@5.0.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ts-api-utils", + "version": "2.5.0", + "purl": "pkg:npm/ts-api-utils@2.5.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "ts-custom-error", + "version": "3.3.1", + "purl": "pkg:npm/ts-custom-error@3.3.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tsconfig-paths", + "version": "3.15.0", + "purl": "pkg:npm/tsconfig-paths@3.15.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "tslib", + "version": "2.8.1", + "purl": "pkg:npm/tslib@2.8.1", + "licenses": [ + { + "expression": "0BSD" + } + ] + }, + { + "type": "library", + "name": "tsx", + "version": "4.23.13", + "purl": "pkg:npm/tsx@4.23.13", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "type-check", + "version": "0.4.0", + "purl": "pkg:npm/type-check@0.4.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "type-fest", + "version": "0.20.2", + "purl": "pkg:npm/type-fest@0.20.2", + "licenses": [ + { + "expression": "(MIT OR CC0-1.0)" + } + ] + }, + { + "type": "library", + "name": "type-fest", + "version": "5.8.0", + "purl": "pkg:npm/type-fest@5.8.0", + "licenses": [ + { + "expression": "(MIT OR CC0-1.0)" + } + ] + }, + { + "type": "library", + "name": "typed-array-buffer", + "version": "1.0.3", + "purl": "pkg:npm/typed-array-buffer@1.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "typed-array-byte-length", + "version": "1.0.3", + "purl": "pkg:npm/typed-array-byte-length@1.0.3", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "typed-array-byte-offset", + "version": "1.0.4", + "purl": "pkg:npm/typed-array-byte-offset@1.0.4", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "typed-array-length", + "version": "1.0.8", + "purl": "pkg:npm/typed-array-length@1.0.8", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "typescript", + "version": "5.9.3", + "purl": "pkg:npm/typescript@5.9.3", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "unbox-primitive", + "version": "1.1.0", + "purl": "pkg:npm/unbox-primitive@1.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "undici-types", + "version": "6.21.0", + "purl": "pkg:npm/undici-types@6.21.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "unrs-resolver", + "version": "1.12.2", + "purl": "pkg:npm/unrs-resolver@1.12.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "uri-js", + "version": "4.4.1", + "purl": "pkg:npm/uri-js@4.4.1", + "licenses": [ + { + "expression": "BSD-2-Clause" + } + ] + }, + { + "type": "library", + "name": "vite", + "version": "8.2.2", + "purl": "pkg:npm/vite@8.2.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "vitest", + "version": "4.1.11", + "purl": "pkg:npm/vitest@4.1.11", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "which-boxed-primitive", + "version": "1.1.1", + "purl": "pkg:npm/which-boxed-primitive@1.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "which-builtin-type", + "version": "1.2.1", + "purl": "pkg:npm/which-builtin-type@1.2.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "which-collection", + "version": "1.0.2", + "purl": "pkg:npm/which-collection@1.0.2", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "which-module", + "version": "2.0.1", + "purl": "pkg:npm/which-module@2.0.1", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "which-typed-array", + "version": "1.1.22", + "purl": "pkg:npm/which-typed-array@1.1.22", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "which", + "version": "2.0.2", + "purl": "pkg:npm/which@2.0.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "why-is-node-running", + "version": "2.3.0", + "purl": "pkg:npm/why-is-node-running@2.3.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "word-wrap", + "version": "1.2.5", + "purl": "pkg:npm/word-wrap@1.2.5", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "wrap-ansi", + "version": "6.2.0", + "purl": "pkg:npm/wrap-ansi@6.2.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "wrappy", + "version": "1.0.2", + "purl": "pkg:npm/wrappy@1.0.2", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "y18n", + "version": "4.0.3", + "purl": "pkg:npm/y18n@4.0.3", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "yargs-parser", + "version": "18.1.3", + "purl": "pkg:npm/yargs-parser@18.1.3", + "licenses": [ + { + "expression": "ISC" + } + ] + }, + { + "type": "library", + "name": "yargs", + "version": "15.4.1", + "purl": "pkg:npm/yargs@15.4.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "yocto-queue", + "version": "0.1.0", + "purl": "pkg:npm/yocto-queue@0.1.0", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "zxing-wasm", + "version": "3.1.1", + "purl": "pkg:npm/zxing-wasm@3.1.1", + "licenses": [ + { + "expression": "MIT" + } + ] + }, + { + "type": "library", + "name": "zxing-cpp", + "version": "6c2961d2a9ea4bc4e4ae8f37b1497299f04dd861", + "purl": "pkg:github/zxing-cpp/zxing-cpp@6c2961d2a9ea4bc4e4ae8f37b1497299f04dd861", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "androidx.camera", + "version": "1.5.0", + "purl": "pkg:maven/androidx.camera/camera-core@1.5.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "androidx.lifecycle", + "version": "2.9.2", + "purl": "pkg:maven/androidx.lifecycle/lifecycle-runtime-ktx@2.9.2", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + }, + { + "type": "library", + "name": "androidx.core", + "version": "1.17.0", + "purl": "pkg:maven/androidx.core/core-ktx@1.17.0", + "licenses": [ + { + "expression": "Apache-2.0" + } + ] + } + ] +} diff --git a/release/stable/v2.1.0/signing-manifest.json b/release/stable/v2.1.0/signing-manifest.json new file mode 100644 index 0000000..7e6c004 --- /dev/null +++ b/release/stable/v2.1.0/signing-manifest.json @@ -0,0 +1,87 @@ +{ + "schemaVersion": "scanly-stable-signing-manifest-1", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "secretMaterialCommitted": false, + "phase": "QUALIFIED_FOR_PUBLICATION", + "channels": { + "gitTagSigning": { + "status": "GIT_TAG_SIGNING_GO", + "scheme": "SSH_ED25519", + "githubAccount": "Yangjunjie-Lin", + "githubSigningKeyId": 1118906, + "publicKeyFingerprint": "SHA256:1odyhkHV3hKHFlE8QrELlBGMM/qlM5ftmiiVym/M6ck", + "publicKeyRegisteredAt": "2026-08-18T01:00:29.903+01:00", + "localSmokeTest": { + "status": "PASS", + "annotatedTagObjectCreated": true, + "sshSignatureBlockPresent": true, + "temporaryTagDeleted": true, + "tagObject": "60d17d680d457103be803e2e0f789320b1a00521" + } + }, + "githubReleaseSigning": { + "status": "GITHUB_RELEASE_SIGNING_GO", + "policy": "GitHub-verified signed annotated tag plus checksums.sha256", + "signedTagRequiredAtPublication": true, + "checksumsPath": "release/stable/v2.1.0/checksums.sha256" + }, + "npmPublication": { + "status": "NPM_PUBLICATION_GO", + "registry": "https://registry.npmjs.org/", + "account": "yangjunjielin", + "organization": "scanly", + "organizationRole": "owner", + "trustedPublisherStatus": "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", + "trustedPublisherPackageCount": 10, + "trustedPublisherPackages": [ + "@scanly/parsers", + "@scanly/scenario-schema", + "@scanly/benchmark", + "@scanly/core", + "@scanly/engine-jsqr", + "@scanly/engine-zxing-js", + "@scanly/engine-zxing-cpp-wasm", + "@scanly/browser", + "@scanly/node", + "@scanly/react" + ], + "repository": "Yangjunjie-Lin/Scanly", + "workflowFile": "stable-npm-publish.yml", + "secretValueRecorded": false, + "provenanceWorkflow": ".github/workflows/stable-npm-publish.yml", + "provenanceMechanism": "GITHUB_ACTIONS_OIDC", + "qualificationRun": 34723767114, + "bootstrap": { + "package": "@scanly/url-safety", + "version": "2.1.0", + "sourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "method": "INTERACTIVE_NPM_PROVENANCE_FILE", + "cliAuthenticatedAccount": "yangjunjielin", + "interactiveTwoFactorRequired": true, + "npmCredentialSharedWithCI": false, + "provenanceVerified": true, + "signedReleaseTagRequired": true, + "placeholderVersionAllowed": false, + "artifactPath": "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", + "provenancePath": "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", + "artifactSha256": "72bbd5974b2a0b0c2c1ded6098c249084df0fbffe50a937ef96e68902b914a87", + "artifactSha512": "269e2f2ed7dbb71926b1eaf33e06df8ed17cf1a0aa16b741252fa2c59a3929588d7549e5aa6655d142bff1b19803db8ad5bf3949562df4d65eba53a84d531c5e", + "provenanceSha256": "d1dbb1347fad221ca90770d8bfbd681f6813a7b70b6237e79f983d1ffc47eb87" + } + }, + "androidArtifactSigning": { + "status": "ANDROID_ARTIFACT_SIGNING_GO", + "distribution": "ANDROID_AAR_GITHUB_RELEASE_ONLY", + "signedTagRequiredAtPublication": true, + "artifactSha256": "44297a5a8b89762a68ccdde61cf107661c8dc7e2b6eb1117f534ce1c67c5098b" + }, + "iosSpmRelease": { + "status": "IOS_SPM_RELEASE_GO", + "distribution": "SIGNED_GIT_TAG_AND_SWIFT_PACKAGE_MANAGER", + "signedTagRequiredAtPublication": true + } + }, + "status": "STABLE_SIGNING_GO" +} diff --git a/release/stable/v2.1.0/v2.1.0-manifest.json b/release/stable/v2.1.0/v2.1.0-manifest.json new file mode 100644 index 0000000..436b4c3 --- /dev/null +++ b/release/stable/v2.1.0/v2.1.0-manifest.json @@ -0,0 +1,137 @@ +{ + "schemaVersion": "scanly-stable-manifest-1", + "version": "2.1.0", + "identity": { + "productSourceCommit": "6b017b53a4404f4feab776ac111b0ea9dacf2734", + "sourceTree": "c72430a2ab0a2d47e7cd095325088f1e053a5c41", + "branch": "develop", + "source": "STABLE_SOURCE_COMMIT" + }, + "software": "GO", + "manifestIntegrity": "GO", + "apiAbi": "GO", + "security": "GO", + "sbom": "GO", + "licenses": "GO", + "artifacts": "GO", + "reproducibility": "GO", + "signing": "GO", + "publicationCredentials": "GO", + "publication": "GO", + "publicationPhase": "QUALIFIED_FOR_PUBLICATION", + "deployment": "GO", + "physicalValidation": { + "requiredForPublication": false, + "status": "POST_RELEASE_VALIDATION_PENDING", + "issue": 13 + }, + "stable": "V2_STABLE_RELEASE_GO", + "blockers": [], + "files": { + "qualification": { + "path": "release/stable/v2.1.0/qualification-input.json", + "sha256": "a42ec5c90457b5f14290ac585e083bbb63e74f540110b05771b90a1bcc8272d6", + "size": 15611 + }, + "qualification_software": { + "path": "release/stable/v2.1.0/evidence/software.json", + "sha256": "e8d507f8d5075a698f20604b4293a2db0f7cd86eb6c489ab4dfaae743325c1c5", + "size": 3222 + }, + "qualification_apiAbi": { + "path": "release/stable/v2.1.0/evidence/api-abi.json", + "sha256": "260cf19c4e9b73ef5b72d64246fa206cf91504e50c9a468dba3c03b698cbc7e7", + "size": 529 + }, + "qualification_security": { + "path": "release/stable/v2.1.0/evidence/security.json", + "sha256": "16e712700e702c1b8cc7c3e6e7fc34069cc08da86a30de72c9528d58a9da692a", + "size": 1555 + }, + "qualification_sbom": { + "path": "release/stable/v2.1.0/sbom.cdx.json", + "sha256": "2b204af3904f6994eb4c69535cbdda4e79cb320f89dc09b798e87dfc0bd99697", + "size": 122332 + }, + "qualification_licenses": { + "path": "release/stable/v2.1.0/license-inventory.json", + "sha256": "929a90ce66b7605fdb09ddd6aabbc2780c8a9aeb97867df030517234d6fd00ff", + "size": 79377 + }, + "qualification_native": { + "path": "release/stable/v2.1.0/android-build-evidence.json", + "sha256": "eb59c7dca88456d929b6c7dd74b670183a94d9024a805c2461f0a9d04eeba991", + "size": 4206 + }, + "qualification_npmReproducibility": { + "path": "release/stable/v2.1.0/evidence/npm-reproducibility.json", + "sha256": "e23e014b2144e54be9742cdc359f97121b40a32790b20d563ecbfe73cc8ee357", + "size": 10041 + }, + "qualification_signing": { + "path": "release/stable/v2.1.0/evidence/signing.json", + "sha256": "fd8e68b7434883e8b88ecb7b337043a9776db439247c7a89d670285bc8b474e8", + "size": 585 + }, + "qualification_publicationCredentials": { + "path": "release/stable/v2.1.0/evidence/publication-credentials-input.json", + "sha256": "5518f3e2970c33fe560996efc523dfb0df22df08773ca835a48274b6c492f123", + "size": 2031 + }, + "qualification_deployment": { + "path": "release/stable/v2.1.0/evidence/deployment-input.json", + "sha256": "85fb809977b41f8e3484d642d13bfac116e2733c5bdb8fa62163863573aa03df", + "size": 431 + }, + "artifactManifest": { + "path": "release/stable/v2.1.0/artifact-manifest.json", + "sha256": "9b7b07cc8d23950ffb0c6b26b4c10f706f42a6338c2cc27dfe228f762badddc4", + "size": 10239 + }, + "androidBuildEvidence": { + "path": "release/stable/v2.1.0/android-build-evidence.json", + "sha256": "eb59c7dca88456d929b6c7dd74b670183a94d9024a805c2461f0a9d04eeba991", + "size": 4206 + }, + "sbom": { + "path": "release/stable/v2.1.0/sbom.cdx.json", + "sha256": "2b204af3904f6994eb4c69535cbdda4e79cb320f89dc09b798e87dfc0bd99697", + "size": 122332 + }, + "licenses": { + "path": "release/stable/v2.1.0/license-inventory.json", + "sha256": "929a90ce66b7605fdb09ddd6aabbc2780c8a9aeb97867df030517234d6fd00ff", + "size": 79377 + }, + "reproducibility": { + "path": "release/stable/v2.1.0/reproducibility.json", + "sha256": "21e10a04433ba57c6d1396bc13126e75cc29c2d7d1b1f21f1e2562468738b2a3", + "size": 6886 + }, + "signing": { + "path": "release/stable/v2.1.0/signing-manifest.json", + "sha256": "dc435518ff3ce80ecabb6ff6780df7a63648bdadcf3903aafadc3f2393d81987", + "size": 3631 + }, + "publicationCredentials": { + "path": "release/stable/v2.1.0/publication-credentials.json", + "sha256": "6e1b1df97fc4fa822ecc7182840ddcf3c910b315d61204d50c9bd1b70d60ba2d", + "size": 4005 + }, + "releasePolicy": { + "path": "release/stable/v2.1.0/release-policy.json", + "sha256": "a0599ed33282bd6361cf0bd979369c3b553fe7a2cd7f3c8b09df61e172bbb49d", + "size": 890 + }, + "physicalValidation": { + "path": "release/stable/v2.1.0/physical-validation-status.json", + "sha256": "c7dc34a4f7517e83a5c120700898e8ebbb90fa49464c19415205144a633f99f3", + "size": 1101 + }, + "deployment": { + "path": "release/stable/v2.1.0/deployment.json", + "sha256": "e77a2eb2001dfa04a42ca86a9293643ddd04d9b85cdc583cc58a81a05488bf50", + "size": 508 + } + } +} diff --git a/release/stable/v2.1.0/v2.1.0-manifest.json.sha256 b/release/stable/v2.1.0/v2.1.0-manifest.json.sha256 new file mode 100644 index 0000000..df7d54e --- /dev/null +++ b/release/stable/v2.1.0/v2.1.0-manifest.json.sha256 @@ -0,0 +1 @@ +89faaff164bafde1033264380f5d19b7c1c168f97d4fc4120a4f4c695686e142 v2.1.0-manifest.json diff --git a/scripts/assemble-v2.1-release-inputs.mjs b/scripts/assemble-v2.1-release-inputs.mjs new file mode 100644 index 0000000..f2f1136 --- /dev/null +++ b/scripts/assemble-v2.1-release-inputs.mjs @@ -0,0 +1,92 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync, spawnSync } from "node:child_process"; +import { createRequire } from "node:module"; + +const root = process.cwd(); +const source = process.argv.find((arg) => arg.startsWith("--source="))?.slice(9); +assert.match(source ?? "", /^[a-f0-9]{40}$/); +const target = "release/stable/v2.1.0"; +assert.ok(!fs.existsSync(target), "Never overwrite an existing candidate or released record"); +assert.notEqual(spawnSync("git", ["show-ref", "--verify", "--quiet", "refs/tags/v2.1.0"]).status, 0, "The release tag already exists"); +const git = (...args) => execFileSync("git", args, { encoding: "utf8" }).trim(); +const tree = git("show", "-s", "--format=%T", source); +const read = (file) => JSON.parse(fs.readFileSync(file, "utf8")); +const digest = (file, algorithm = "sha256") => crypto.createHash(algorithm).update(fs.readFileSync(file)).digest("hex"); +const write = (name, data) => fs.writeFileSync(`${target}/${name}`, JSON.stringify(data, null, 2) + "\n"); +const software = read("release-dist/v2.1-software-evidence.json"); +const repro = read("release-dist/v2.1-npm-reproducibility.json"); +const native = read("release-dist/v2.1-candidate/android-build-evidence.json"); +const security = read("release-dist/v2.1-source-ci-evidence/development/url-safety-security.json"); +const oidc = read("release-dist/v2.1-oidc-qualified/npm-oidc-qualification.json"); +const deployment = read("release-dist/v2.1-deployment-evidence.json"); +for (const data of [software, repro, native, security, deployment]) assert.equal(data.sourceCommit, source); +assert.equal(software.status, "PASS"); assert.equal(software.runs.length, 8); +assert.ok(software.runs.every((run) => run.headSha === source && run.conclusion === "success")); +assert.equal(repro.status, "PASS"); assert.equal(repro.rawBuildAEqualsBuildB, true); assert.equal(repro.packageCount, 11); +assert.equal(native.status, "GO"); assert.equal(native.normalizedBuildAEqualsBuildB, true); +assert.notEqual(native.cleanBuildA.runId, native.cleanBuildB.runId); +assert.equal(security.result, "URL_SAFETY_SECURITY_GO"); assert.equal(security.repositoryDirty, false); +assert.equal(oidc.existingTenAccepted, true); assert.equal(oidc.configurationModified, false); +assert.equal(deployment.readyState, "READY"); assert.equal(deployment.target, "production"); +assert.ok(deployment.alias.includes("qr-decoder-theta.vercel.app")); +const npmCli = process.env.NPM_CLI_PATH; +assert.ok(npmCli, "NPM_CLI_PATH is required"); +const runNpm = (...args) => execFileSync(process.execPath, [npmCli, ...args], { encoding: "utf8" }).trim(); +const account = runNpm("whoami", "--fetch-retries=0", "--fetch-timeout=15000"); +assert.equal(account, "yangjunjielin"); +assert.equal(JSON.parse(runNpm("org", "ls", "scanly", "--json", "--fetch-retries=0", "--fetch-timeout=15000"))[account], "owner"); +const probeTag = "60d17d680d457103be803e2e0f789320b1a00521"; +assert.equal(git("cat-file", "-t", probeTag), "tag"); assert.equal(git("rev-parse", `${probeTag}^{}`), source); +const signature = execFileSync("git", ["-c", "gpg.ssh.allowedSignersFile=release-dist/v2.1-signing/allowed-signers", "verify-tag", probeTag], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); +void signature; +const signingKeys = JSON.parse(execFileSync("gh", ["api", "user/ssh_signing_keys"], { encoding: "utf8" })); +const signingKey = signingKeys.find((key) => key.id === 1118906); +assert.ok(signingKey?.key && fs.readFileSync("release-dist/v2.1-signing/allowed-signers", "utf8").includes(signingKey.key)); +const bootstrapArtifact = read("release-dist/v2.1-build-A/build.json").artifacts.find((artifact) => artifact.name === "@scanly/url-safety"); +const bootstrapProof = repro.detachedProvenanceVerified.find((proof) => proof.package === "@scanly/url-safety"); +assert.ok(bootstrapArtifact && bootstrapProof); +assert.equal(bootstrapArtifact.sha256, bootstrapProof.artifactSha256); +const requireNpm = createRequire(npmCli); +const { verifyProvenance } = requireNpm(path.join(path.dirname(requireNpm.resolve("libnpmpublish")), "provenance.js")); +const bundle = await verifyProvenance({ name: bootstrapArtifact.purl, digest: { sha512: bootstrapArtifact.sha512 } }, bootstrapProof.file); +await requireNpm("sigstore").verify(bundle, { certificateIssuer: "https://token.actions.githubusercontent.com", certificateIdentityURI: bootstrapProof.certificateIdentity }); + +fs.mkdirSync(`${target}/evidence`, { recursive: true }); +fs.cpSync("release-dist/v2.1-candidate/artifacts", `${target}/artifacts`, { recursive: true, errorOnExist: true }); +for (const name of ["sbom.cdx.json", "license-inventory.json", "android-build-evidence.json"]) fs.copyFileSync(`release-dist/v2.1-candidate/${name}`, `${target}/${name}`); +const evidenceFiles = { + "software.json": "release-dist/v2.1-software-evidence.json", + "security.json": "release-dist/v2.1-source-ci-evidence/development/url-safety-security.json", + "url-safety-tests.json": "release-dist/v2.1-source-ci-evidence/development/url-safety-tests.json", + "url-safety-benchmark.json": "release-dist/v2.1-source-ci-evidence/development/url-safety-benchmark.json", + "npm-reproducibility.json": "release-dist/v2.1-npm-reproducibility.json", + "npm-build-run.json": "release-dist/v2.1-npm-run-evidence.json", + "native-build-a.json": "release-dist/v2.1-native-A-run.json", + "native-build-b.json": "release-dist/v2.1-native-B-run.json", + "npm-oidc-qualification.json": "release-dist/v2.1-oidc-qualified/npm-oidc-qualification.json", + "deployment-input.json": "release-dist/v2.1-deployment-evidence.json", + "allowed-signers": "release-dist/v2.1-signing/allowed-signers", +}; +for (const [name, file] of Object.entries(evidenceFiles)) fs.copyFileSync(file, `${target}/evidence/${name}`); +fs.writeFileSync(`${target}/evidence/signing-probe.tag`, execFileSync("git", ["cat-file", "tag", probeTag])); +const signingEvidence = { scheme: "SSH_ED25519", githubAccount: "Yangjunjie-Lin", githubSigningKeyId: signingKey.id, publicKeyFingerprint: "SHA256:1odyhkHV3hKHFlE8QrELlBGMM/qlM5ftmiiVym/M6ck", publicKeyRegisteredAt: signingKey.created_at, localSmokeTest: { status: "PASS", annotatedTagObjectCreated: true, sshSignatureBlockPresent: true, temporaryTagDeleted: true, tagObject: probeTag } }; +write("evidence/signing.json", { status: "PASS", sourceCommit: source, evidence: signingEvidence }); +write("evidence/api-abi.json", { status: "PASS", sourceCommit: source, publicApi: { path: "api-snapshots/public-api.json", sha256: digest("api-snapshots/public-api.json") }, nativeApi: { path: "api-snapshots/native-api.json", sha256: digest("api-snapshots/native-api.json") }, nativeAbi: { path: "api-snapshots/native-abi.json", sha256: digest("api-snapshots/native-abi.json") } }); +const publisherPackages = oidc.results.filter((entry) => entry.status === "OIDC_TOKEN_EXCHANGE_ACCEPTED").map((entry) => entry.package); +assert.equal(publisherPackages.length, 10); +const npmPublicationEvidence = { + registry: "https://registry.npmjs.org/", account, organization: "scanly", organizationRole: "owner", trustedPublisherStatus: "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", trustedPublisherPackageCount: 10, trustedPublisherPackages: publisherPackages, + repository: "Yangjunjie-Lin/Scanly", workflowFile: "stable-npm-publish.yml", secretValueRecorded: false, provenanceWorkflow: ".github/workflows/stable-npm-publish.yml", provenanceMechanism: "GITHUB_ACTIONS_OIDC", qualificationRun: oidc.runId, + bootstrap: { package: "@scanly/url-safety", version: "2.1.0", sourceCommit: source, method: "INTERACTIVE_NPM_PROVENANCE_FILE", cliAuthenticatedAccount: account, interactiveTwoFactorRequired: true, npmCredentialSharedWithCI: false, provenanceVerified: true, signedReleaseTagRequired: true, placeholderVersionAllowed: false, artifactPath: `${target}/artifacts/npm/${bootstrapArtifact.filename}`, provenancePath: `${target}/artifacts/provenance/${bootstrapArtifact.provenanceFile}`, artifactSha256: bootstrapArtifact.sha256, artifactSha512: bootstrapArtifact.sha512, provenanceSha256: bootstrapArtifact.provenanceSha256 }, +}; +write("evidence/publication-credentials-input.json", { status: "PASS", sourceCommit: source, checkedAt: new Date().toISOString(), evidence: npmPublicationEvidence, publicationPerformed: false }); +const gate = (file) => ({ status: "PASS", sourceCommit: source, path: `${target}/${file}`, sha256: digest(`${target}/${file}`) }); +const qualification = { schemaVersion: "scanly-release-qualification-1", version: "2.1.0", sourceCommit: source, sourceTree: tree, + gates: { software: gate("evidence/software.json"), apiAbi: gate("evidence/api-abi.json"), security: gate("evidence/security.json"), sbom: gate("sbom.cdx.json"), licenses: gate("license-inventory.json"), native: gate("android-build-evidence.json"), npmReproducibility: gate("evidence/npm-reproducibility.json"), signing: gate("evidence/signing.json"), publicationCredentials: gate("evidence/publication-credentials-input.json"), deployment: gate("evidence/deployment-input.json") }, + signingEvidence, npmPublicationEvidence, npmReproducibility: repro, +}; +write("qualification-input.json", qualification); +console.log(`Prepared source-bound qualification inputs at ${target}. No signed release tag or publication was created.`); diff --git a/scripts/confirm-stable-metadata-reproducibility.mjs b/scripts/confirm-stable-metadata-reproducibility.mjs index dcd1832..517bd13 100644 --- a/scripts/confirm-stable-metadata-reproducibility.mjs +++ b/scripts/confirm-stable-metadata-reproducibility.mjs @@ -33,11 +33,17 @@ const comparedFiles = [ "artifacts/ios/Package.swift", "artifacts/native/scanly-core.h", ]; +if (version === "2.1.0") { + comparedFiles.push("qualification-input.json", "artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json"); + const visit = (directory) => fs.readdirSync(path.join(directoryA, directory), { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? visit(`${directory}/${entry.name}`) : [`${directory}/${entry.name}`]); + comparedFiles.push(...visit("evidence").sort()); +} const npmArtifacts = fs.readdirSync(path.join(directoryA, "artifacts", "npm"), { withFileTypes: true }) .filter((entry) => entry.isFile() && entry.name.endsWith(".tgz")) .map((entry) => `artifacts/npm/${entry.name}`) .sort(); -if (npmArtifacts.length !== 10) throw new Error(`Expected ten clean-build npm artifacts, found ${npmArtifacts.length}.`); +const expectedNpmCount = version === "2.0.0" || version === "2.0.1" ? 10 : 11; +if (npmArtifacts.length !== expectedNpmCount) throw new Error(`Expected ${expectedNpmCount} clean-build npm artifacts, found ${npmArtifacts.length}.`); comparedFiles.push(...npmArtifacts); const digest = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); for (const relative of comparedFiles) { diff --git a/scripts/generate-stable-release-manifest.mjs b/scripts/generate-stable-release-manifest.mjs index 2875405..12b3727 100644 --- a/scripts/generate-stable-release-manifest.mjs +++ b/scripts/generate-stable-release-manifest.mjs @@ -2,6 +2,7 @@ import crypto from "node:crypto"; import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; +import { validateStableNpmCredentials } from "./stable-npm-credential-policy.mjs"; import { canonicalNpmTarballSha256, canonicalZipSha256, @@ -27,12 +28,24 @@ const sourceCommit = process.env.STABLE_SOURCE_COMMIT const sourceTree = process.env.STABLE_SOURCE_TREE ?? execFileSync("git", ["rev-parse", "HEAD^{tree}"], { cwd: root, encoding: "utf8" }).trim(); const historicalVersion = version === "2.0.0" || version === "2.0.1"; +const prepareArtifactsOnly = process.argv.includes("--prepare-artifacts"); +if (prepareArtifactsOnly && version !== "2.1.0") throw new Error("Artifact preparation is restricted to the new 2.1.0 candidate."); const expectedPublicPackageCount = historicalVersion ? 10 : 11; +if (execFileSync("git", ["show", "-s", "--format=%T", sourceCommit], { cwd: root, encoding: "utf8" }).trim() !== sourceTree) throw new Error("Source commit/tree mismatch."); +if (!historicalVersion) { + const productPaths = ["apps", "packages", "engines", "native", "package-lock.json", "api-snapshots"]; + execFileSync("git", ["diff", "--exit-code", sourceCommit, "HEAD", "--", ...productPaths], { cwd: root, stdio: "pipe" }); + execFileSync("git", ["diff", "--exit-code", "HEAD", "--", ...productPaths], { cwd: root, stdio: "pipe" }); + const sourcePackage = JSON.parse(execFileSync("git", ["show", `${sourceCommit}:package.json`], { cwd: root, encoding: "utf8" })); + const qualifiedPackage = structuredClone(rootManifest); + if (!sourcePackage.scripts?.["stable:bootstrap"] && qualifiedPackage.scripts?.["stable:bootstrap"] === "node scripts/publish-v2.1-bootstrap.mjs") delete qualifiedPackage.scripts["stable:bootstrap"]; + if (JSON.stringify(sourcePackage) !== JSON.stringify(qualifiedPackage)) throw new Error("Product package metadata changed beyond the approved release bootstrap tooling command."); +} // Never carry legacy hardcoded qualification/signing/credential claims into a // new minor release. Require exact-source, content-addressed evidence BEFORE // creating any artifact or metadata file. let qualification; -if (!historicalVersion) { +if (!historicalVersion && !prepareArtifactsOnly) { if (!process.env.STABLE_QUALIFICATION_RECORD) throw new Error("STABLE_QUALIFICATION_RECORD is required for new releases; legacy GO claims cannot be inherited."); qualification = JSON.parse(fs.readFileSync(path.resolve(root, process.env.STABLE_QUALIFICATION_RECORD), "utf8")); if (qualification.schemaVersion !== "scanly-release-qualification-1" || qualification.version !== version || qualification.sourceCommit !== sourceCommit || qualification.sourceTree !== sourceTree) throw new Error("Release qualification source identity mismatch."); @@ -43,7 +56,7 @@ if (!historicalVersion) { if (!evidencePath.startsWith(root + path.sep) || !fs.existsSync(evidencePath) || crypto.createHash("sha256").update(fs.readFileSync(evidencePath)).digest("hex") !== evidence.sha256) throw new Error(`Release qualification ${gate} evidence integrity failed.`); } if (!qualification.signingEvidence?.localSmokeTest?.sshSignatureBlockPresent || qualification.signingEvidence?.localSmokeTest?.status !== "PASS") throw new Error("Fresh signing evidence is required."); - if (qualification.npmPublicationEvidence?.trustedPublisherStatus !== "AVAILABLE" || qualification.npmPublicationEvidence?.trustedPublisherPackageCount !== expectedPublicPackageCount || qualification.npmPublicationEvidence?.secretValueRecorded !== false) throw new Error("Fresh 11-package Trusted Publishing credential evidence is required."); + validateStableNpmCredentials(qualification.npmPublicationEvidence, version, expectedPublicPackageCount, sourceCommit); if (!qualification.npmReproducibility?.cleanBuildA || !qualification.npmReproducibility?.cleanBuildB || qualification.npmReproducibility.cleanBuildRunA === qualification.npmReproducibility.cleanBuildRunB) throw new Error("Two independent npm reproducibility builds are required."); } const sourceTimestamp = execFileSync("git", ["show", "-s", "--format=%cI", sourceCommit], { cwd: root, encoding: "utf8" }).trim(); @@ -52,7 +65,7 @@ const requiredDeploymentValue = (name, legacyValue) => { if (!value) throw new Error(`${name} is required when generating Stable ${version} evidence.`); return value; }; -const stableDeployment = { +const stableDeployment = prepareArtifactsOnly ? undefined : { schemaVersion: "scanly-stable-deployment-1", version, sourceCommit, @@ -79,6 +92,13 @@ const fileIdentity = (relative) => { return { path: relative.replaceAll("\\", "/"), sha256: sha256(bytes), size: bytes.length }; }; +if (qualification?.npmPublicationEvidence.bootstrap) { + const bootstrap = qualification.npmPublicationEvidence.bootstrap; + if (fileIdentity(bootstrap.artifactPath).sha256 !== bootstrap.artifactSha256 || fileIdentity(bootstrap.provenancePath).sha256 !== bootstrap.provenanceSha256) throw new Error("Bootstrap artifact/provenance bytes do not match qualified evidence."); + const bytes = fs.readFileSync(resolveRelative(bootstrap.artifactPath)); + if (crypto.createHash("sha512").update(bytes).digest("hex") !== bootstrap.artifactSha512) throw new Error("Bootstrap SHA-512 mismatch."); +} + fs.mkdirSync(path.join(artifactsRoot, "ios"), { recursive: true }); fs.mkdirSync(path.join(artifactsRoot, "native"), { recursive: true }); fs.writeFileSync(path.join(artifactsRoot, "ios", "Package.swift"), fs.readFileSync(path.join(root, "native", "ios", "Package.swift"), "utf8").replaceAll("\r\n", "\n")); @@ -210,15 +230,16 @@ const components = Object.entries(lock.packages ?? {}) .map(([relative, metadata]) => { const packagePath = path.join(root, relative, "package.json"); const packageJson = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, "utf8")) : {}; - const name = packageJson.name ?? relative.replace(/^node_modules\//, ""); + const name = packageJson.name ?? relative.slice(relative.lastIndexOf("node_modules/") + "node_modules/".length); const version = packageJson.version ?? metadata.version ?? "UNKNOWN"; const license = typeof metadata.license === "string" ? metadata.license : typeof packageJson.license === "string" ? packageJson.license - : /^@(?:emnapi|esbuild|napi-rs|next|rolldown|rollup|tybys|unrs)\//.test(name) ? "MIT" + : !historicalVersion ? "NOASSERTION" + : /^@(?:emnapi|esbuild|napi-rs|next|rolldown|rollup|tybys|unrs)\//.test(name) ? "MIT" : name === "fsevents" || name.endsWith("/node_modules/fsevents") ? "MIT" : name.startsWith("lightningcss-") ? "MPL-2.0" : name.startsWith("@img/sharp") ? "Apache-2.0" : "NOASSERTION"; - return { name, version, license, purl: `pkg:npm/${encodeURIComponent(name)}@${version}` }; + return { name, version, license, purl: `pkg:npm/${name.split("/").map(encodeURIComponent).join("/")}@${version}` }; }) .sort((a, b) => `${a.name}@${a.version}`.localeCompare(`${b.name}@${b.version}`)); const nativeComponents = [ @@ -227,7 +248,7 @@ const nativeComponents = [ { name: "androidx.lifecycle", version: "2.9.2", license: "Apache-2.0", purl: "pkg:maven/androidx.lifecycle/lifecycle-runtime-ktx@2.9.2" }, { name: "androidx.core", version: "1.17.0", license: "Apache-2.0", purl: "pkg:maven/androidx.core/core-ktx@1.17.0" }, ]; -const allComponents = [...components, ...nativeComponents]; +const allComponents = [...new Map([...components, ...nativeComponents].map((component) => [component.purl, component])).values()]; const unknownLicenses = allComponents.filter((component) => component.license === "NOASSERTION"); const sbom = { bomFormat: "CycloneDX", @@ -260,6 +281,11 @@ writeJson("license-inventory.json", { unknownLicenseCount: unknownLicenses.length, components: allComponents, }); +if (prepareArtifactsOnly) { + if (unknownLicenses.length) throw new Error("Unresolved licenses in candidate inventory."); + console.log(`Prepared ${packageArtifacts.length} npm artifacts, Native identities, SBOM and licenses for ${sourceCommit}; no Stable GO manifest or publication was created.`); + process.exit(0); +} const physical = { schemaVersion: "scanly-stable-physical-validation-status-1", @@ -453,6 +479,10 @@ const manifest = { ...(!reproducibilityGo ? ["BLOCKED_REPRODUCIBILITY_FINALIZATION"] : []), ], files: { + ...(qualification ? { + qualification: fileIdentity(`${stablePrefix}qualification-input.json`), + ...Object.fromEntries(Object.entries(qualification.gates).map(([gate, evidence]) => [`qualification_${gate}`, fileIdentity(evidence.path)])), + } : {}), artifactManifest: fileIdentity(`${stablePrefix}artifact-manifest.json`), ...(androidEvidencePresent ? { androidBuildEvidence: fileIdentity(`${stablePrefix}android-build-evidence.json`) } : {}), sbom: fileIdentity(`${stablePrefix}sbom.cdx.json`), @@ -470,6 +500,7 @@ writeJson(manifestName, manifest); const checksumPaths = [ ...packageArtifacts.map((entry) => entry.path), + ...(qualification?.npmPublicationEvidence.bootstrap ? [qualification.npmPublicationEvidence.bootstrap.provenancePath] : []), `${stablePrefix}artifacts/ios/Package.swift`, `${stablePrefix}artifacts/native/scanly-core.h`, ...(androidArtifactPresent ? [androidArtifactRelative] : []), diff --git a/scripts/prepare-v2.1-npm-artifacts.mjs b/scripts/prepare-v2.1-npm-artifacts.mjs new file mode 100644 index 0000000..27f7710 --- /dev/null +++ b/scripts/prepare-v2.1-npm-artifacts.mjs @@ -0,0 +1,87 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; +import { createRequire } from "node:module"; + +const args = Object.fromEntries(process.argv.slice(2).map((arg) => { + const separator = arg.indexOf("="); + assert.ok(arg.startsWith("--") && separator > 2, "Use --name=value arguments"); + return [arg.slice(2, separator), arg.slice(separator + 1)]; +})); +const root = fs.realpathSync(path.resolve(args.root ?? ".")); +const output = path.resolve(args.output ?? "release-dist/v2.1-npm"); +const git = (...values) => execFileSync("git", values, { cwd: root, encoding: "utf8" }).trim(); +const sourceCommit = git("rev-parse", "HEAD"); +const sourceTree = git("rev-parse", "HEAD^{tree}"); +assert.equal(sourceCommit, process.env.SOURCE_COMMIT); +assert.equal(git("status", "--porcelain", "--untracked-files=all"), "", "Product checkout must be clean"); +assert.equal(process.env.GITHUB_ACTIONS, "true", "Detached provenance requires a real GitHub Actions runner"); +assert.equal(process.env.GITHUB_REPOSITORY, "Yangjunjie-Lin/Scanly"); +assert.equal(process.env.RUNNER_ENVIRONMENT, "github-hosted"); +assert.ok(!process.env.NODE_AUTH_TOKEN && !process.env.NPM_TOKEN, "No npm credentials are used by this builder"); +assert.ok(process.env.NPM_CLI_PATH && process.env.ACTIONS_ID_TOKEN_REQUEST_URL); +const npmRequire = createRequire(process.env.NPM_CLI_PATH); +const sigstore = npmRequire("sigstore"); +const npa = npmRequire("npm-package-arg"); +const npmVersion = execFileSync(process.execPath, [process.env.NPM_CLI_PATH, "--version"], { encoding: "utf8" }).trim(); +assert.equal(npmVersion, "11.12.1"); +const version = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version; +assert.equal(version, "2.1.0"); +assert.ok(!fs.existsSync(output), "Never overwrite an existing artifact build"); +fs.mkdirSync(path.join(output, "npm"), { recursive: true }); +fs.mkdirSync(path.join(output, "provenance")); +const sha = (bytes, algorithm = "sha256") => crypto.createHash(algorithm).update(bytes).digest("hex"); +const records = []; +const workflowRef = process.env.GITHUB_WORKFLOW_REF; +assert.ok(workflowRef?.startsWith("Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@")); +const workflowPath = ".github/workflows/v2.1-artifact-qualification.yml"; +const workflowGitRef = workflowRef.slice(workflowRef.indexOf("@") + 1); +const invocationId = `https://github.com/Yangjunjie-Lin/Scanly/actions/runs/${process.env.GITHUB_RUN_ID}/attempts/${process.env.GITHUB_RUN_ATTEMPT}`; + +for (const folder of ["packages", "engines"]) { + for (const entry of fs.readdirSync(path.join(root, folder), { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) { + const directory = path.join(root, folder, entry.name); + const manifestPath = path.join(directory, "package.json"); + if (!entry.isDirectory() || !fs.existsSync(manifestPath)) continue; + const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + if (manifest.private) continue; + assert.equal(manifest.version, version); + assert.equal(manifest.repository?.url, "git+https://github.com/Yangjunjie-Lin/Scanly.git"); + const [packed] = JSON.parse(execFileSync(process.execPath, [process.env.NPM_CLI_PATH, "pack", "--json", "--pack-destination", path.join(output, "npm")], { cwd: directory, encoding: "utf8" })); + const bytes = fs.readFileSync(path.join(output, "npm", packed.filename)); + const purl = npa.toPurl(npa(`${manifest.name}@${version}`)); + // The workflow definition and checked-out product source are explicitly + // different dependencies. Never spoof GITHUB_SHA or claim a different source. + const statement = { + _type: "https://in-toto.io/Statement/v1", + subject: [{ name: purl, digest: { sha512: sha(bytes, "sha512") } }], + predicateType: "https://slsa.dev/provenance/v1", + predicate: { + buildDefinition: { + buildType: "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1", + externalParameters: { workflow: { ref: workflowGitRef, repository: "https://github.com/Yangjunjie-Lin/Scanly", path: workflowPath }, source_commit: sourceCommit }, + internalParameters: { github: { event_name: process.env.GITHUB_EVENT_NAME, repository_id: process.env.GITHUB_REPOSITORY_ID, repository_owner_id: process.env.GITHUB_REPOSITORY_OWNER_ID } }, + resolvedDependencies: [ + { uri: `git+https://github.com/Yangjunjie-Lin/Scanly@${sourceCommit}`, digest: { gitCommit: sourceCommit } }, + { uri: `git+https://github.com/Yangjunjie-Lin/Scanly@${workflowGitRef}`, digest: { gitCommit: process.env.GITHUB_SHA } }, + ], + }, + runDetails: { builder: { id: "https://github.com/actions/runner/github-hosted" }, metadata: { invocationId } }, + }, + }; + const bundle = await sigstore.attest(Buffer.from(JSON.stringify(statement)), "application/vnd.in-toto+json"); + await sigstore.verify(bundle); + const provenanceFile = `${packed.filename}.sigstore.json`; + const serialized = JSON.stringify(bundle, null, 2) + "\n"; + fs.writeFileSync(path.join(output, "provenance", provenanceFile), serialized); + records.push({ name: manifest.name, version, filename: packed.filename, size: bytes.length, sha256: sha(bytes), sha512: sha(bytes, "sha512"), integrity: packed.integrity, provenanceFile, provenanceSha256: sha(serialized), purl }); + } +} +assert.equal(records.length, 11); +assert.ok(records.some((record) => record.name === "@scanly/url-safety")); +const report = { schemaVersion: "scanly-v2.1-npm-build-1", version, sourceCommit, sourceTree, workflowDefinitionCommit: process.env.GITHUB_SHA, workflowRef, buildLabel: process.env.BUILD_LABEL, runId: Number(process.env.GITHUB_RUN_ID), runAttempt: Number(process.env.GITHUB_RUN_ATTEMPT), invocationId, nodeVersion: process.version, npmVersion, repositoryDirty: false, published: false, artifacts: records.sort((a, b) => a.name.localeCompare(b.name)) }; +fs.writeFileSync(path.join(output, "build.json"), JSON.stringify(report, null, 2) + "\n"); +fs.writeFileSync(path.join(output, "checksums.sha256"), records.map((record) => `${record.sha256} npm/${record.filename}`).sort().join("\n") + "\n"); +console.log(`Prepared ${records.length} exact npm artifacts and verified detached Sigstore bundles; no package was published.`); diff --git a/scripts/probe-stable-npm-oidc.mjs b/scripts/probe-stable-npm-oidc.mjs new file mode 100644 index 0000000..84b9eed --- /dev/null +++ b/scripts/probe-stable-npm-oidc.mjs @@ -0,0 +1,29 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; + +assert.equal(process.env.GITHUB_ACTIONS, "true"); +assert.equal(process.env.GITHUB_REPOSITORY, "Yangjunjie-Lin/Scanly"); +assert.equal(process.env.RUNNER_ENVIRONMENT, "github-hosted"); +assert.ok(process.env.GITHUB_WORKFLOW_REF?.startsWith("Yangjunjie-Lin/Scanly/.github/workflows/stable-npm-publish.yml@")); +assert.ok(!process.env.NODE_AUTH_TOKEN && !process.env.NPM_TOKEN, "No stored npm credentials are permitted"); +const names = ["@scanly/parsers", "@scanly/scenario-schema", "@scanly/benchmark", "@scanly/core", "@scanly/engine-jsqr", "@scanly/engine-zxing-js", "@scanly/engine-zxing-cpp-wasm", "@scanly/browser", "@scanly/node", "@scanly/react", "@scanly/url-safety"]; +const tokenUrl = new URL(process.env.ACTIONS_ID_TOKEN_REQUEST_URL); +tokenUrl.searchParams.set("audience", "npm:registry.npmjs.org"); +const identityResponse = await fetch(tokenUrl, { headers: { Authorization: `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`, Accept: "application/json" }, signal: AbortSignal.timeout(15_000) }); +assert.ok(identityResponse.ok, "GitHub OIDC identity request failed"); +const identity = await identityResponse.json(); +assert.ok(typeof identity.value === "string", "GitHub OIDC identity missing"); +const results = []; +for (const name of names) { + // This is the same official exchange used by npm's OIDC authentication. + // Returned credentials remain in memory and are never logged or persisted. + const response = await fetch(`https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(name)}`, { method: "POST", headers: { Authorization: `Bearer ${identity.value}`, Accept: "application/json" }, signal: AbortSignal.timeout(15_000) }); + const body = await response.json().catch(() => ({})); + const accepted = response.ok && typeof body.token === "string" && body.token.length > 0; + results.push({ package: name, status: accepted ? "OIDC_TOKEN_EXCHANGE_ACCEPTED" : name === "@scanly/url-safety" && response.status === 404 ? "FIRST_PUBLICATION_BOOTSTRAP_REQUIRED" : "UNAVAILABLE", httpStatus: response.status, credentialRecorded: false }); + console.log(`${name}: ${results.at(-1).status} (HTTP ${response.status})`); +} +const report = { schemaVersion: "scanly-npm-oidc-qualification-1", checkedAt: new Date().toISOString(), repository: process.env.GITHUB_REPOSITORY, workflowFile: "stable-npm-publish.yml", workflowRef: process.env.GITHUB_WORKFLOW_REF, workflowDefinitionCommit: process.env.GITHUB_SHA, runId: Number(process.env.GITHUB_RUN_ID), runAttempt: Number(process.env.GITHUB_RUN_ATTEMPT), storedNpmCredentialsUsed: false, credentialsRecorded: false, configurationModified: false, packagesPublished: [], results, existingTenAccepted: results.slice(0, 10).every((item) => item.status === "OIDC_TOKEN_EXCHANGE_ACCEPTED"), limitation: "Exchange acceptance proves the matching existing trusted identity can obtain a short-lived npm credential. Final package publication and provenance still require separate verification." }; +fs.mkdirSync("release-dist", { recursive: true }); +fs.writeFileSync("release-dist/npm-oidc-qualification.json", JSON.stringify(report, null, 2) + "\n"); +assert.ok(report.existingTenAccepted, "Existing trusted publisher qualification failed"); diff --git a/scripts/publish-v2.1-bootstrap.mjs b/scripts/publish-v2.1-bootstrap.mjs new file mode 100644 index 0000000..8f6535b --- /dev/null +++ b/scripts/publish-v2.1-bootstrap.mjs @@ -0,0 +1,52 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; +import { createRequire } from "node:module"; + +const root = process.cwd(); +const tag = "v2.1.0"; +const directory = "release/stable/v2.1.0"; +const git = (...args) => execFileSync("git", args, { encoding: "utf8" }).trim(); +const npmCli = process.env.npm_execpath; +assert.ok(npmCli, "Run through npm run stable:bootstrap"); +const runNpm = (...args) => execFileSync(process.execPath, [npmCli, ...args], { encoding: "utf8" }).trim(); +assert.equal(git("cat-file", "-t", tag), "tag"); +execFileSync("git", ["-c", `gpg.ssh.allowedSignersFile=${directory}/evidence/allowed-signers`, "verify-tag", tag], { stdio: "inherit" }); +const manifestFile = `${directory}/${tag}-manifest.json`; +const taggedManifest = execFileSync("git", ["show", `${tag}:${manifestFile}`]); +assert.ok(taggedManifest.equals(fs.readFileSync(manifestFile)), "Working manifest differs from the signed tag"); +execFileSync(process.execPath, ["scripts/verify-stable-manifest.mjs", "--require-go"], { stdio: "inherit", env: { ...process.env, STABLE_RELEASE_VERSION: "2.1.0" } }); +const release = JSON.parse(execFileSync("gh", ["release", "view", tag, "--repo", "Yangjunjie-Lin/Scanly", "--json", "tagName,isDraft,isPrerelease,url"], { encoding: "utf8" })); +assert.equal(release.tagName, tag); assert.equal(release.isDraft, false); assert.equal(release.isPrerelease, false); +const credentials = JSON.parse(fs.readFileSync(`${directory}/publication-credentials.json`, "utf8")); +const bootstrap = credentials.channels.npmRegistry.evidence.bootstrap; +assert.equal(bootstrap.package, "@scanly/url-safety"); assert.equal(bootstrap.version, "2.1.0"); +const artifactFile = path.resolve(root, bootstrap.artifactPath); +const provenanceFile = path.resolve(root, bootstrap.provenancePath); +const bytes = fs.readFileSync(artifactFile); +assert.equal(crypto.createHash("sha256").update(bytes).digest("hex"), bootstrap.artifactSha256); +assert.equal(crypto.createHash("sha512").update(bytes).digest("hex"), bootstrap.artifactSha512); +assert.equal(crypto.createHash("sha256").update(fs.readFileSync(provenanceFile)).digest("hex"), bootstrap.provenanceSha256); +const requireNpm = createRequire(npmCli); +const verifier = requireNpm(path.join(path.dirname(requireNpm.resolve("libnpmpublish")), "provenance.js")); +await verifier.verifyProvenance({ name: "pkg:npm/%40scanly/url-safety@2.1.0", digest: { sha512: bootstrap.artifactSha512 } }, provenanceFile); +assert.equal(runNpm("whoami"), "yangjunjielin"); +const artifacts = JSON.parse(fs.readFileSync(`${directory}/artifact-manifest.json`, "utf8")).artifacts; +const parser = artifacts.find((artifact) => artifact.platform === "npm" && artifact.path.endsWith("scanly-parsers-2.1.0.tgz")); +assert.ok(parser); +const expectedParserIntegrity = `sha512-${crypto.createHash("sha512").update(fs.readFileSync(parser.path)).digest("base64")}`; +assert.equal(JSON.parse(runNpm("view", "@scanly/parsers@2.1.0", "dist.integrity", "--json")), expectedParserIntegrity, "Publish and verify the parser dependency first"); +const expectedIntegrity = `sha512-${Buffer.from(bootstrap.artifactSha512, "hex").toString("base64")}`; +let existing; +try { existing = JSON.parse(runNpm("view", "@scanly/url-safety@2.1.0", "dist", "--json")); } catch { /* first publication */ } +if (!existing) { + console.log("Publishing only @scanly/url-safety@2.1.0, with qualified exact bytes and verified CI provenance. npm may require interactive security-key confirmation."); + execFileSync(process.execPath, [npmCli, "publish", artifactFile, "--access=public", "--tag=latest", `--provenance-file=${provenanceFile}`, "--browser=false"], { stdio: "inherit" }); +} +const dist = JSON.parse(runNpm("view", "@scanly/url-safety@2.1.0", "dist", "--json")); +assert.equal(dist.integrity, expectedIntegrity); +assert.equal(dist.attestations?.provenance?.predicateType, "https://slsa.dev/provenance/v1"); +assert.equal(JSON.parse(runNpm("view", "@scanly/url-safety", "dist-tags.latest", "--json")), "2.1.0"); +console.log("V2_1_BOOTSTRAP_PUBLICATION_VERIFIED: exact registry integrity, latest tag, and provenance present."); diff --git a/scripts/rebuild-v2.1-release-metadata.mjs b/scripts/rebuild-v2.1-release-metadata.mjs new file mode 100644 index 0000000..62f6f13 --- /dev/null +++ b/scripts/rebuild-v2.1-release-metadata.mjs @@ -0,0 +1,45 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +const root = process.cwd(); +const canonical = "release/stable/v2.1.0"; +const qualificationFile = `${canonical}/qualification-input.json`; +const qualification = JSON.parse(fs.readFileSync(qualificationFile, "utf8")); +const deployment = JSON.parse(fs.readFileSync(`${canonical}/evidence/deployment-input.json`, "utf8")); +assert.equal(qualification.version, "2.1.0"); +assert.equal(deployment.sourceCommit, qualification.sourceCommit); +const env = { ...process.env, STABLE_RELEASE_VERSION: "2.1.0", STABLE_SOURCE_COMMIT: qualification.sourceCommit, STABLE_SOURCE_TREE: qualification.sourceTree, STABLE_QUALIFICATION_RECORD: qualificationFile, STABLE_DEPLOYMENT_ID: deployment.id, STABLE_DEPLOYMENT_URL: `https://${deployment.url}`, STABLE_PRODUCTION_ALIAS: "https://qr-decoder-theta.vercel.app", STABLE_DEPLOYMENT_COMMIT: deployment.sourceCommit }; +delete env.STABLE_OUTPUT_ROOT; +const base = fs.mkdtempSync(path.join(root, "release-dist", "v2.1-metadata-")); +const hash = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); +const generate = (output) => execFileSync(process.execPath, ["scripts/generate-stable-release-manifest.mjs"], { env: { ...env, ...(output ? { STABLE_OUTPUT_ROOT: path.relative(root, output) } : {}) }, stdio: "inherit" }); +const build = (label) => { + const output = path.join(base, label); + fs.mkdirSync(output); + for (const directory of ["artifacts", "evidence"]) fs.cpSync(`${canonical}/${directory}`, path.join(output, directory), { recursive: true, errorOnExist: true }); + for (const file of ["qualification-input.json", "android-build-evidence.json"]) fs.copyFileSync(`${canonical}/${file}`, path.join(output, file)); + generate(output); + return output; +}; +const confirm = (a, b) => execFileSync(process.execPath, ["scripts/confirm-stable-metadata-reproducibility.mjs", `--build-a-dir=${a}`, `--build-b-dir=${b}`], { env, stdio: "inherit" }); +const preliminaryA = build("preliminary-A"); +const preliminaryB = build("preliminary-B"); +confirm(preliminaryA, preliminaryB); +qualification.gates.native.sha256 = hash(`${canonical}/android-build-evidence.json`); +fs.writeFileSync(qualificationFile, JSON.stringify(qualification, null, 2) + "\n"); +generate(); +const finalA = build("final-A"); +const finalB = build("final-B"); +confirm(finalA, finalB); +assert.equal(qualification.gates.native.sha256, hash(`${canonical}/android-build-evidence.json`), "Final metadata confirmation unexpectedly changed the bound Native evidence"); +generate(); +const evidence = JSON.parse(fs.readFileSync(`${canonical}/android-build-evidence.json`, "utf8")); +for (const file of evidence.metadataCleanBuilds.comparedFiles) { + assert.equal(hash(path.join(finalA, file)), hash(path.join(finalB, file)), `Final builds differ: ${file}`); + assert.equal(hash(path.join(finalA, file)), hash(`${canonical}/${file}`), `Selected canonical metadata differs: ${file}`); +} +execFileSync(process.execPath, ["scripts/verify-stable-manifest.mjs", "--require-go"], { env, stdio: "inherit" }); +console.log(`V2_1_FINAL_METADATA_REPRODUCIBILITY_GO: both final GO outputs and selected canonical bytes match (${evidence.metadataCleanBuilds.comparedFiles.length} files). Independent build directories: ${base}`); diff --git a/scripts/record-stable-android-artifact.mjs b/scripts/record-stable-android-artifact.mjs index 089cf09..fa2e103 100644 --- a/scripts/record-stable-android-artifact.mjs +++ b/scripts/record-stable-android-artifact.mjs @@ -1,6 +1,7 @@ import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; +import { execFileSync } from "node:child_process"; import { canonicalZipSha256, validatedZipEntries, @@ -34,10 +35,26 @@ if (!/^\d+$/.test(runA) || !/^\d+$/.test(runB) || !/^[a-f0-9]{40}$/.test(workflo throw new Error("Run IDs or commit identities are malformed."); } -const stableManifest = JSON.parse(fs.readFileSync(path.join(stableRoot, `v${version}-manifest.json`), "utf8")); -const sourceCommit = stableManifest.identity?.productSourceCommit; -const sourceTree = stableManifest.identity?.sourceTree; +const manifestPath = path.join(stableRoot, `v${version}-manifest.json`); +const stableManifest = fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, "utf8")) : undefined; +// Initial artifact qualification must precede the final Stable manifest. +// Derive the source tree from the exact requested commit; never invent a GO +// manifest merely to satisfy this recorder's input dependency. +const sourceCommit = stableManifest?.identity?.productSourceCommit ?? requestedSourceCommit; +const sourceTree = stableManifest?.identity?.sourceTree ?? execFileSync("git", ["show", "-s", "--format=%T", requestedSourceCommit], { cwd: root, encoding: "utf8" }).trim(); if (requestedSourceCommit !== sourceCommit) throw new Error("Android workflow source does not match STABLE_SOURCE_COMMIT."); +if (version === "2.1.0") { + const sourcePackage = JSON.parse(execFileSync("git", ["show", `${sourceCommit}:package.json`], { cwd: root, encoding: "utf8" })); + if (sourcePackage.version !== version || workflowHeadSha !== sourceCommit) throw new Error("Native source version or workflow definition identity mismatch."); + if (runA === runB) throw new Error("Independent Native workflow runs are required."); + for (const [label, id] of [["a", runA], ["b", runB]]) { + const run = JSON.parse(fs.readFileSync(path.resolve(root, requiredArgument(`run-${label}-evidence`)), "utf8")); + if (String(run.databaseId) !== id || run.headSha !== sourceCommit || run.status !== "completed" || run.conclusion !== "success" || run.workflowName !== "Native Mobile") throw new Error(`Native build ${label} workflow evidence is not an exact-source success.`); + for (const name of ["Native Core", "iOS SDK", "Android SDK", "Native Memory", "Native Fixture Parity", "Native Artifact Validation"]) { + if (!run.jobs?.some((job) => job.name === name && job.conclusion === "success")) throw new Error(`Native build ${label} is missing passing ${name} evidence.`); + } + } +} const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); const inspectAar = (file) => { if (!fs.statSync(file).isFile()) throw new Error(`${file}: AAR is missing.`); diff --git a/scripts/stable-npm-credential-policy.mjs b/scripts/stable-npm-credential-policy.mjs new file mode 100644 index 0000000..8923c3a --- /dev/null +++ b/scripts/stable-npm-credential-policy.mjs @@ -0,0 +1,42 @@ +import assert from "node:assert/strict"; + +const existingNames = ["@scanly/benchmark", "@scanly/browser", "@scanly/core", "@scanly/engine-jsqr", "@scanly/engine-zxing-cpp-wasm", "@scanly/engine-zxing-js", "@scanly/node", "@scanly/parsers", "@scanly/react", "@scanly/scenario-schema"]; + +/** Preserve normal OIDC policy. The only bootstrap exception is the new 2.1.0 + * URL package, with an authenticated owner, interactive 2FA, and an exact-byte + * CI-signed provenance bundle. It is never a token-in-CI or provenance waiver. */ +export function validateStableNpmCredentials(evidence, version, expectedCount, sourceCommit) { + assert.equal(evidence?.account, "yangjunjielin"); + assert.equal(evidence?.organization, "scanly"); + assert.equal(evidence?.organizationRole, "owner"); + assert.equal(evidence?.repository, "Yangjunjie-Lin/Scanly"); + assert.equal(evidence?.workflowFile, "stable-npm-publish.yml"); + assert.equal(evidence?.provenanceMechanism, "GITHUB_ACTIONS_OIDC"); + assert.equal(evidence?.secretValueRecorded, false); + if (!evidence.bootstrap) { + assert.equal(evidence.trustedPublisherStatus, "AVAILABLE"); + assert.equal(evidence.trustedPublisherPackageCount, expectedCount); + return "OIDC"; + } + assert.equal(version, "2.1.0", "The first-publish exception is version-scoped"); + assert.equal(expectedCount, 11); + assert.equal(evidence.trustedPublisherStatus, "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP"); + assert.equal(evidence.trustedPublisherPackageCount, 10); + assert.deepEqual([...evidence.trustedPublisherPackages].sort(), existingNames); + const bootstrap = evidence.bootstrap; + assert.equal(bootstrap.package, "@scanly/url-safety"); + assert.equal(bootstrap.version, version); + assert.equal(bootstrap.sourceCommit, sourceCommit); + assert.equal(bootstrap.method, "INTERACTIVE_NPM_PROVENANCE_FILE"); + assert.equal(bootstrap.cliAuthenticatedAccount, evidence.account); + assert.equal(bootstrap.interactiveTwoFactorRequired, true); + assert.equal(bootstrap.npmCredentialSharedWithCI, false); + assert.equal(bootstrap.provenanceVerified, true); + assert.equal(bootstrap.signedReleaseTagRequired, true); + assert.equal(bootstrap.placeholderVersionAllowed, false); + assert.equal(bootstrap.artifactPath, "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz"); + assert.equal(bootstrap.provenancePath, "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json"); + for (const key of ["artifactSha256", "provenanceSha256"]) assert.match(bootstrap[key] ?? "", /^[a-f0-9]{64}$/); + assert.match(bootstrap.artifactSha512 ?? "", /^[a-f0-9]{128}$/); + return "OIDC_WITH_INTERACTIVE_FIRST_PACKAGE"; +} diff --git a/scripts/verify-stable-manifest.mjs b/scripts/verify-stable-manifest.mjs index 7b42aba..a835d7f 100644 --- a/scripts/verify-stable-manifest.mjs +++ b/scripts/verify-stable-manifest.mjs @@ -3,6 +3,7 @@ import fs from "node:fs"; import path from "node:path"; import { execFileSync } from "node:child_process"; import { canonicalZipSha256 } from "./release-artifact-canonicalization.mjs"; +import { validateStableNpmCredentials } from "./stable-npm-credential-policy.mjs"; const root = path.resolve(import.meta.dirname, ".."); const rootManifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")); @@ -67,12 +68,15 @@ if (publicationCredentials.requiredCredentialsStatus === "GO") { } if (publicationCredentials.blocker) fail("Publication credential GO cannot retain a blocker."); const npmEvidence = publicationCredentials.channels.npmRegistry.evidence; - if (npmEvidence?.account !== "yangjunjielin" || npmEvidence?.organization !== "scanly" - || npmEvidence?.organizationRole !== "owner" || npmEvidence?.trustedPublisherStatus !== "AVAILABLE" - || npmEvidence?.trustedPublisherPackageCount !== expectedPublicPackageCount || npmEvidence?.repository !== "Yangjunjie-Lin/Scanly" - || npmEvidence?.workflowFile !== "stable-npm-publish.yml" - || npmEvidence?.provenanceMechanism !== "GITHUB_ACTIONS_OIDC" - || npmEvidence?.secretValueRecorded !== false) fail("npm publication credential evidence is incomplete."); + validateStableNpmCredentials(npmEvidence, version, expectedPublicPackageCount, sourceCommit); + if (npmEvidence.bootstrap) { + const bootstrap = npmEvidence.bootstrap; + if (!exists(bootstrap.provenancePath) || !exists(bootstrap.artifactPath) || sha256(bootstrap.provenancePath) !== bootstrap.provenanceSha256 || sha256(bootstrap.artifactPath) !== bootstrap.artifactSha256) fail("Bootstrap file identities do not match qualified evidence."); + const bundle = readJson(bootstrap.provenancePath); + const statement = JSON.parse(Buffer.from(bundle.dsseEnvelope?.payload ?? "", "base64").toString("utf8")); + if (statement.subject?.length !== 1 || statement.subject[0]?.name !== "pkg:npm/%40scanly/url-safety@2.1.0" || statement.subject[0]?.digest?.sha512 !== bootstrap.artifactSha512 || statement.predicateType !== "https://slsa.dev/provenance/v1" || statement.predicate?.buildDefinition?.externalParameters?.source_commit !== sourceCommit) fail("Bootstrap provenance subject or source mismatch."); + if (!Array.isArray(bundle.dsseEnvelope?.signatures) || !bundle.dsseEnvelope.signatures.length || !bundle.verificationMaterial?.tlogEntries?.length) fail("Bootstrap provenance lacks signatures or transparency-log evidence."); + } } const stableNpmWorkflowPath = ".github/workflows/stable-npm-publish.yml"; if (!exists(stableNpmWorkflowPath)) fail("Stable npm provenance publication workflow is missing."); @@ -119,6 +123,16 @@ if (androidArtifact?.status === "PASS") { for (const file of Object.values(manifest.files ?? {})) { if (!exists(file.path) || sha256(file.path) !== file.sha256 || fs.statSync(path.join(root, file.path)).size !== file.size) fail(`Stable Manifest file identity mismatch: ${file.path}`); } +if (version === "2.1.0") { + if (manifest.files.qualification?.path !== stablePath("qualification-input.json")) fail("Stable 2.1.0 must bind its exact-source qualification inputs."); + const qualification = readJson(manifest.files.qualification.path); + if (qualification.sourceCommit !== sourceCommit || qualification.sourceTree !== sourceTree || qualification.version !== version) fail("Bound qualification source mismatch."); + for (const gate of ["software", "apiAbi", "security", "sbom", "licenses", "native", "npmReproducibility", "signing", "publicationCredentials", "deployment"]) { + const evidence = qualification.gates?.[gate]; + const bound = manifest.files[`qualification_${gate}`]; + if (evidence?.status !== "PASS" || evidence.sourceCommit !== sourceCommit || bound?.path !== evidence.path || bound.sha256 !== evidence.sha256) fail(`Qualification gate ${gate} is not bound to passing evidence.`); + } +} const manifestName = `v${version}-manifest.json`; if (!exists(stablePath("checksums.sha256")) || !exists(stablePath(`${manifestName}.sha256`))) fail("Stable checksum files are missing."); const checksumLines = fs.readFileSync(path.join(stableRoot, "checksums.sha256"), "utf8").trim().split(/\r?\n/); diff --git a/scripts/verify-v2.1-artifact-builds.mjs b/scripts/verify-v2.1-artifact-builds.mjs new file mode 100644 index 0000000..aa685fd --- /dev/null +++ b/scripts/verify-v2.1-artifact-builds.mjs @@ -0,0 +1,85 @@ +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import { createRequire } from "node:module"; +import { execFileSync } from "node:child_process"; + +const args = Object.fromEntries(process.argv.slice(2).map((arg) => { + const separator = arg.indexOf("="); + assert.ok(arg.startsWith("--") && separator > 2); + return [arg.slice(2, separator), arg.slice(separator + 1)]; +})); +for (const required of ["build-a", "build-b", "run-evidence", "npm-cli", "output"]) assert.ok(args[required], `Missing --${required}`); +const root = process.cwd(); +const directories = [path.resolve(args["build-a"]), path.resolve(args["build-b"])]; +assert.notEqual(directories[0], directories[1]); +const read = (file) => JSON.parse(fs.readFileSync(file, "utf8")); +const hash = (bytes, algorithm = "sha256") => crypto.createHash(algorithm).update(bytes).digest("hex"); +const reports = directories.map((directory) => read(path.join(directory, "build.json"))); +const run = read(path.resolve(args["run-evidence"])); +assert.equal(run.conclusion, "success"); +assert.equal(run.status, "completed"); +const sourceCommit = reports[0].sourceCommit; +const sourceTree = execFileSync("git", ["show", "-s", "--format=%T", sourceCommit], { encoding: "utf8" }).trim(); +const requireNpm = createRequire(path.resolve(args["npm-cli"])); +const sigstore = requireNpm("sigstore"); +const npa = requireNpm("npm-package-arg"); +const { verifyProvenance } = requireNpm(path.join(path.dirname(requireNpm.resolve("libnpmpublish")), "provenance.js")); +const names = new Set(); +const maps = []; +const provenances = []; +const jobs = []; +for (let i = 0; i < reports.length; i++) { + const report = reports[i]; + const label = i === 0 ? "A" : "B"; + assert.equal(report.schemaVersion, "scanly-v2.1-npm-build-1"); + assert.equal(report.version, "2.1.0"); + assert.equal(report.sourceCommit, sourceCommit); + assert.equal(report.sourceTree, sourceTree); + assert.equal(report.repositoryDirty, false); + assert.equal(report.published, false); + assert.equal(report.buildLabel, label); + assert.equal(report.runId, run.databaseId); + assert.equal(report.artifacts.length, 11); + assert.ok(report.workflowRef.startsWith("Yangjunjie-Lin/Scanly/.github/workflows/v2.1-artifact-qualification.yml@")); + const job = run.jobs.find((entry) => entry.name === `Clean npm build ${label}`); + assert.equal(job?.conclusion, "success"); + assert.ok(job.databaseId); + jobs.push(job.databaseId); + const hashes = {}; + for (const artifact of report.artifacts) { + for (const value of [artifact.filename, artifact.provenanceFile]) assert.equal(path.basename(value), value); + const bytes = fs.readFileSync(path.join(directories[i], "npm", artifact.filename)); + assert.equal(bytes.length, artifact.size); + assert.equal(hash(bytes), artifact.sha256); + assert.equal(hash(bytes, "sha512"), artifact.sha512); + assert.equal(artifact.version, "2.1.0"); + assert.equal(artifact.purl, npa.toPurl(npa(`${artifact.name}@2.1.0`))); + assert.ok(artifact.name.startsWith("@scanly/")); + assert.equal(hashes[artifact.filename], undefined, "Duplicate package artifact"); + hashes[artifact.filename] = artifact.sha256; + const file = path.join(directories[i], "provenance", artifact.provenanceFile); + assert.equal(hash(fs.readFileSync(file)), artifact.provenanceSha256); + const bundle = await verifyProvenance({ name: artifact.purl, digest: { sha512: artifact.sha512 } }, file); + await sigstore.verify(bundle, { + certificateIssuer: "https://token.actions.githubusercontent.com", + certificateIdentityURI: `https://github.com/${report.workflowRef}`, + }); + const statement = JSON.parse(Buffer.from(bundle.dsseEnvelope.payload, "base64").toString("utf8")); + assert.equal(statement.predicateType, "https://slsa.dev/provenance/v1"); + assert.equal(statement.predicate.buildDefinition.externalParameters.source_commit, sourceCommit); + assert.ok(statement.predicate.buildDefinition.resolvedDependencies.some((dependency) => dependency.digest.gitCommit === sourceCommit)); + if (i === 0) { names.add(artifact.name); provenances.push({ package: artifact.name, file: path.relative(root, file).replaceAll("\\", "/"), sha256: artifact.provenanceSha256, artifactSha256: artifact.sha256, artifactSha512: artifact.sha512, certificateIdentity: `https://github.com/${report.workflowRef}`, verified: true }); } + } + maps.push(hashes); +} +assert.notEqual(jobs[0], jobs[1], "Independent GitHub-hosted build jobs are required"); +assert.deepEqual(maps[0], maps[1]); +assert.equal(names.size, 11); +assert.ok(names.has("@scanly/url-safety")); +const result = { schemaVersion: "scanly-v2.1-npm-reproducibility-1", version: "2.1.0", sourceCommit, sourceTree, status: "PASS", independentBuildJobs: jobs, runId: run.databaseId, cleanBuildRunA: `https://github.com/Yangjunjie-Lin/Scanly/actions/runs/${run.databaseId}/job/${jobs[0]}`, cleanBuildRunB: `https://github.com/Yangjunjie-Lin/Scanly/actions/runs/${run.databaseId}/job/${jobs[1]}`, cleanBuildA: maps[0], cleanBuildB: maps[1], rawBuildAEqualsBuildB: true, detachedProvenanceVerified: provenances, packageCount: names.size, published: false }; +const output = path.resolve(args.output); +fs.mkdirSync(path.dirname(output), { recursive: true }); +fs.writeFileSync(output, JSON.stringify(result, null, 2) + "\n"); +console.log("V2_1_NPM_ARTIFACT_REPRODUCIBILITY_GO: 11 byte-identical tarballs; 22 issuer/identity/subject-bound provenance bundles verified. No publication performed."); diff --git a/tests/unit/stable-npm-credential-policy.test.ts b/tests/unit/stable-npm-credential-policy.test.ts new file mode 100644 index 0000000..028ed1a --- /dev/null +++ b/tests/unit/stable-npm-credential-policy.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; +import fs from "node:fs"; +// JavaScript release tooling is deliberately independent of the SDK bundle. +// @ts-expect-error release MJS has no TypeScript declaration entry +import { validateStableNpmCredentials } from "../../scripts/stable-npm-credential-policy.mjs"; + +const base = { account: "yangjunjielin", organization: "scanly", organizationRole: "owner", repository: "Yangjunjie-Lin/Scanly", workflowFile: "stable-npm-publish.yml", provenanceMechanism: "GITHUB_ACTIONS_OIDC", secretValueRecorded: false, trustedPublisherStatus: "AVAILABLE", trustedPublisherPackageCount: 11 }; +const source = "a".repeat(40); +const bootstrap = () => ({ ...base, trustedPublisherStatus: "AVAILABLE_WITH_QUALIFIED_BOOTSTRAP", trustedPublisherPackageCount: 10, trustedPublisherPackages: ["@scanly/benchmark", "@scanly/browser", "@scanly/core", "@scanly/engine-jsqr", "@scanly/engine-zxing-cpp-wasm", "@scanly/engine-zxing-js", "@scanly/node", "@scanly/parsers", "@scanly/react", "@scanly/scenario-schema"], bootstrap: { package: "@scanly/url-safety", version: "2.1.0", sourceCommit: source, method: "INTERACTIVE_NPM_PROVENANCE_FILE", cliAuthenticatedAccount: "yangjunjielin", interactiveTwoFactorRequired: true, npmCredentialSharedWithCI: false, provenanceVerified: true, signedReleaseTagRequired: true, placeholderVersionAllowed: false, artifactPath: "release/stable/v2.1.0/artifacts/npm/scanly-url-safety-2.1.0.tgz", provenancePath: "release/stable/v2.1.0/artifacts/provenance/scanly-url-safety-2.1.0.tgz.sigstore.json", artifactSha256: "b".repeat(64), provenanceSha256: "c".repeat(64), artifactSha512: "d".repeat(128) } }); +describe("version-scoped first-publication credential policy", () => { + it("requires an actual release event and independently prevents publication in qualification-only mode", () => { + const workflow = fs.readFileSync(".github/workflows/stable-npm-publish.yml", "utf8"); + expect(workflow).toContain("github.event_name == 'release' && github.event.release.prerelease == false"); + expect(workflow).toContain('run: test "$QUALIFY_ONLY" != "true"'); + expect(workflow.indexOf("Assert publication mode")).toBeLessThan(workflow.indexOf("name: Resolve and verify signed Stable release identity")); + }); + it("preserves normal and historical OIDC checks", () => { + expect(validateStableNpmCredentials(base, "2.1.0", 11, source)).toBe("OIDC"); + expect(validateStableNpmCredentials({ ...base, trustedPublisherPackageCount: 10 }, "2.0.1", 10, source)).toBe("OIDC"); + expect(() => validateStableNpmCredentials({ ...base, trustedPublisherPackageCount: 10 }, "2.1.0", 11, source)).toThrow(); + }); + it("allows only the exact new package with signed provenance and interactive 2FA", () => { + expect(validateStableNpmCredentials(bootstrap(), "2.1.0", 11, source)).toBe("OIDC_WITH_INTERACTIVE_FIRST_PACKAGE"); + expect(() => validateStableNpmCredentials(bootstrap(), "2.0.1", 10, source)).toThrow(); + }); + it.each(["package", "version", "sourceCommit", "method", "cliAuthenticatedAccount", "artifactPath", "provenancePath", "artifactSha256", "artifactSha512", "provenanceSha256"])("rejects substituted %s", (field) => { + const value = bootstrap(); + Object.assign(value.bootstrap, { [field]: "substituted" }); + expect(() => validateStableNpmCredentials(value, "2.1.0", 11, source)).toThrow(); + }); + it.each(["interactiveTwoFactorRequired", "provenanceVerified", "signedReleaseTagRequired"])("cannot waive %s", (field) => { + const value = bootstrap(); Object.assign(value.bootstrap, { [field]: false }); + expect(() => validateStableNpmCredentials(value, "2.1.0", 11, source)).toThrow(); + }); + it.each(["npmCredentialSharedWithCI", "placeholderVersionAllowed"])("forbids %s", (field) => { + const value = bootstrap(); Object.assign(value.bootstrap, { [field]: true }); + expect(() => validateStableNpmCredentials(value, "2.1.0", 11, source)).toThrow(); + }); + it("rejects unverified or mismatched remaining package publishers", () => { + const value = bootstrap(); value.trustedPublisherPackages.pop(); + expect(() => validateStableNpmCredentials(value, "2.1.0", 11, source)).toThrow(); + expect(() => validateStableNpmCredentials({ ...base, secretValueRecorded: true }, "2.1.0", 11, source)).toThrow(); + }); +});