diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7510c81 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + build-and-test: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Lint + run: npm run lint + + - name: Typecheck + run: npm run typecheck + + - name: Unit tests + run: npm run test:ci + + - name: Build + run: npm run build + + - name: Pack and guard artifacts + run: npm run pack:check diff --git a/.github/workflows/localnet-smoke.yml b/.github/workflows/localnet-smoke.yml new file mode 100644 index 0000000..d2b2da9 --- /dev/null +++ b/.github/workflows/localnet-smoke.yml @@ -0,0 +1,80 @@ +name: LocalNet Smoke + +# LocalNet lifecycle + CIP-56 transfer smoke. Intended to become a required PR check +# once Docker image caching is finalized (TODO(ENG-1635)). +# +# Tradeoff vs nightly-only: first-run image pulls dominate wall time (~10–15m) and need +# Docker/Compose + disk on the runner. We still run on pull_request so LocalNet regressions +# block merges; keep workflow_dispatch for manual re-runs. No scheduled job — PR coverage is +# the regression gate. +# +# Uses Splice TestTokenV2 (not a Fairmint token). +# +# Docker readiness notes: +# - The ubuntu-latest runner usually has Docker available via the Actions service, but LocalNet +# also needs Compose v2, passwordless sudo for host aliases / dockerd bootstrap in constrained +# environments, and enough disk for Splice image pulls. +# - First-run image pulls dominate wall time; prefer a warmed runner cache once the shared LocalNet +# image strategy is finalized (TODO(ENG-1635)). +# - Port 3000 must be free (cn-quickstart nginx binds 127.0.0.1:3000). +# - If nginx was created without a network after a failed start, remove it before retrying: +# `docker rm -f nginx`. + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + localnet-smoke: + runs-on: ubuntu-latest + # Image pulls + DSO bootstrap + CIP-56 transfer routinely exceed 15m on a cold runner. + timeout-minutes: 45 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Fetch Splice TestTokenV2 fixture DAR + run: npm run fixture:splice-test-token-v2:fetch + + # TODO(ENG-1635): harden GH Actions Docker provisioning / image caching for LocalNet. + # This job is best-effort until the shared runner image and cache strategy are finalized. + - name: Start LocalNet + id: localnet-start + run: | + set -euo pipefail + npm run localnet:start + + - name: Readiness + id: localnet-readiness + run: npm run localnet:readiness + + # Prefer a real step with clear failure when LocalNet / Docker is not ready. + # Do NOT invent a Fairmint token — uses fixtures/splice-test-token-v2 (CIP-112 capable). + - name: CIP-56 / CIP-112 transfer smoke (Splice TestTokenV2) + id: cip56-transfer + run: | + set -euo pipefail + npm run localnet:cip56-transfer + + - name: Diagnostics on failure + if: failure() + run: npm run localnet:diagnostics + + - name: Teardown + if: always() + run: npm run localnet:teardown diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..3cb7a97 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,97 @@ +name: Publish to NPM + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +# OIDC trusted publishing on npmjs.com: +# https://www.npmjs.com/package/@fairmint/canton-dev-tools/access (Trusted Publisher section) +# — or create the package first via "Add Trusted Publisher" on the @fairmint org. +# Provider: GitHub Actions +# Repository: Fairmint/canton-dev-tools +# Workflow filename: publish.yml +# The npm CLI auto-detects the OIDC environment and exchanges the GitHub-issued id-token +# for a short-lived registry token, so no long-lived NPM_TOKEN secret is required for publish. +# +# Required to publish: id-token: write +# Required to push the git tag back: contents: write +# +# Install may still use NODE_AUTH_TOKEN (NPM_TOKEN) to fetch private @fairmint/* peers. +# Do NOT set NODE_AUTH_TOKEN / NPM_TOKEN on the Publish step — that breaks OIDC trusted publishing. + +jobs: + publish: + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + permissions: + contents: write + packages: write + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@v7 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + # Trusted publishing requires Node >= 22.14.0 (npm docs) + node-version: '22.14' + registry-url: 'https://registry.npmjs.org' + package-manager-cache: false + + - name: Upgrade npm + # Trusted publishing requires npm CLI >= 11.5.1. Pin below npm 12 until + # npm@latest no longer fails OIDC publishes with a missing sigstore module + # on hosted runners (see npm/cli#8730). + run: npm install --global npm@11.18.0 + + - name: Install dependencies + run: npm ci + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Build package + run: npm run build + + - name: Prepack and check package artifacts + run: npm run check:package-artifacts + + - name: Prepare release + run: npm run prepare-release + + # setup-node's registry-url writes `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` + # into the runner npmrc. With NODE_AUTH_TOKEN unset on this step that expands to empty and + # npm treats classic auth as configured, skipping the OIDC Trusted Publisher exchange. + - name: Clear classic npm auth for OIDC publish + run: | + npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}" + if [[ -f "${npmrc}" ]]; then + sed -i '/_authToken/d' "${npmrc}" + fi + + # Publishes via npm Trusted Publisher (OIDC); do not set NODE_AUTH_TOKEN here. + # Provenance is omitted: this repository may be private, and --provenance requires + # a public source repo. OIDC trusted publishing still works without provenance. + - name: Publish to NPM + run: npm publish + + - name: Create and push git tag + run: | + NEW_VERSION=$(node -p "require('./package.json').version") + CHANGELOG=$(head -100 CHANGELOG.md) + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git tag -a "v$NEW_VERSION" -m "$CHANGELOG" + git push origin "v$NEW_VERSION" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83e2ae0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +node_modules/ +dist/ +coverage/ +*.tgz +.DS_Store +.env +.env.local +*.log +.npmrc +libs/ +artifacts/ +fixtures/**/.daml/ +fixtures/**/*.dar diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5d82119 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 100, + "semi": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..072ff55 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +Release notes are prepended here by `npm run prepare-release` on each publish to npm. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md new file mode 100644 index 0000000..f4c9ee8 --- /dev/null +++ b/COMPATIBILITY.md @@ -0,0 +1,55 @@ +# LocalNet Compatibility Pins + +`@fairmint/canton-dev-tools` is the **central owner** of Fairmint's shared Canton LocalNet pin set. + +Consumers should prefer this package's CLI defaults in `bin/canton-dev-tools` (kept in sync with +`package.json#config` by `pack:check`) instead of maintaining divergent pins in individual SDKs. +`npm run localnet*` delegates to the binary, which applies the four pins and oauth2 auth as +defaults while preserving caller environment overrides. + +| Pin | Config key | Current value | +| ----------------------- | --------------------------- | ------------------------------------------ | +| cn-quickstart git ref | `localnet_quickstart_ref` | `2f4edfc17621a7dfb6d44357050c22f4b3914c89` | +| Splice image tag | `localnet_splice_version` | `0.6.14` | +| Scribe image tag | `localnet_scribe_version` | `0.6.14` | +| Canton protocol version | `localnet_protocol_version` | `35` | + +## Why both quickstart ref and Splice version? + +They are **not redundant** — each owns a different layer: + +| Pin | Owns | Why it is separate | +| ----------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `localnet_quickstart_ref` | CN Quickstart **compose / scripts / Daml runtime wiring** (git tree) | Locks how LocalNet is brought up (ports, modules, Keycloak, `.env` defaults). | +| `localnet_splice_version` / `localnet_scribe_version` | **Container image tags** written into Quickstart `.env` via `CANTON_LOCALNET_*` | Lets us run MainNet-aligned Splice/Scribe images on an older, known-good Quickstart tree without waiting for a Quickstart bump that already embeds that tag. | + +Quickstart's checked-in `.env` may still say `SPLICE_VERSION=0.6.11` at the pinned ref; our CLI **overrides** that with `localnet_splice_version`. Same pattern as `@fairmint/canton-assets` LocalNet docs (same Quickstart ref + MainNet Splice `0.6.14`). + +Do not derive one from the other in this package: consumers may need to advance images before compose, or pin compose while validating a new image. + +## Auth mode + +| Mode | When to use | +| --------------- | ---------------------------------------------------------------------------- | +| `oauth2` | **Package default** for `npm run localnet*` and bare CLI / `npx` invocations | +| `shared-secret` | **Consumer CI** that signs HS256 JWTs (e.g. `@fairmint/canton-fairmint-sdk`) | + +Override with `CANTON_LOCALNET_AUTH_MODE=shared-secret` in consumer packages. Do not change the +package default to shared-secret without coordinating Keycloak / OAuth2 LocalNet smoke coverage. + +## Override env vars + +These remain supported for one-off local experiments: + +- `CANTON_LOCALNET_QUICKSTART_REF` +- `CANTON_LOCALNET_SPLICE_VERSION` +- `CANTON_LOCALNET_SCRIBE_VERSION` +- `CANTON_LOCALNET_PROTOCOL_VERSION` +- `CANTON_LOCALNET_QUICKSTART_DIR` +- `CANTON_LOCALNET_CACHE_DIR` +- `CANTON_LOCALNET_AUTH_MODE` +- `CANTON_LOCALNET_INFRA_ONLY` + +Changing the default pins is a breaking compatibility change for shared LocalNet CI and should be reviewed as part of ENG-1635 follow-ups. + +SDK package-boundary ownership (what ships in publish tarballs vs CI-only fixtures) is tracked via consumer PRs under [ENG-1635](https://linear.app/fairmint/issue/ENG-1635/establish-canton-dev-tools-and-migrate-shared-canton-test). diff --git a/README.md b/README.md new file mode 100644 index 0000000..bffaa8a --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# @fairmint/canton-dev-tools + +Shared Canton LocalNet CLI and integration-test helpers for Fairmint packages. + +This package owns the versioned LocalNet lifecycle (start, readiness, diagnostics, teardown) and reusable TypeScript helpers used by Canton integration tests. It is introduced by [ENG-1635](https://linear.app/fairmint/issue/ENG-1635/establish-canton-dev-tools-and-migrate-shared-canton-test). + +## Docs + +- [Canton LocalNet testing](https://github.com/Fairmint/dev-docs/blob/main/docs/development/testing/canton-localnet.md) +- [COMPATIBILITY.md](./COMPATIBILITY.md) — pinned LocalNet / Splice / scribe / protocol versions and auth defaults + +## Install + +```bash +npm install -D @fairmint/canton-dev-tools +``` + +Peer dependency: `@fairmint/canton-node-sdk` (for TypeScript helpers). + +## CLI + +Product commands: + +```bash +npx canton-dev-tools start +npx canton-dev-tools readiness +npx canton-dev-tools diagnostics +npx canton-dev-tools teardown +``` + +The binary hardcodes the four LocalNet pins from [COMPATIBILITY.md](./COMPATIBILITY.md), so `npx` / +direct invocations apply the same defaults as `npm run localnet*`. + +Auth defaults to **oauth2** (Keycloak). Consumer CI that uses HS256 JWTs (for example +`@fairmint/canton-fairmint-sdk`) should set `CANTON_LOCALNET_AUTH_MODE=shared-secret`. + +Legacy aliases (same binary as `canton-localnet`): `setup`, `stop`, `logs`, `status`, `smoke`, `test`, `verify`. + +Environment variables keep the `CANTON_LOCALNET_*` prefix for consumer compatibility. + +## TypeScript helpers + +```ts +import { + buildLocalnetClientConfig, + getLocalnetParticipantAdminLedgerClient, + findCreatedContractId, + buildTransferFactoryTransferCommand, + SPLICE_TEST_TOKEN_V2_SHA256, +} from '@fairmint/canton-dev-tools/testing'; +``` + +CIP-56 / CIP-112 helpers target the Splice `splice-test-token-v2` reference DAR. + +## Publishing + +Every push/merge to `main` runs [`.github/workflows/publish.yml`](./.github/workflows/publish.yml), +which auto-publishes `@fairmint/canton-dev-tools` to npm via **OIDC Trusted Publishing** (no +`NPM_TOKEN` on the publish step). + +- **`package.json` version is a floor.** Intentional minor/major bumps: set a version higher than + npm's latest before merging; CI publishes that exact version. +- **Routine patches auto-increment** from the higher of npm latest / existing tags. +- Version rewrites happen in the CI workspace only — they are not committed back to `main`. +- Before the first successful publish, a human must configure an npm Trusted Publisher for + `Fairmint/canton-dev-tools` → workflow `publish.yml` on the `@fairmint` org. + +## Development + +```bash +npm install +npm run build +npm test +npm run pack:check +npm run fixture:splice-test-token-v2:fetch +``` + +### CIP-56 / CIP-112 LocalNet transfer smoke + +Uses Splice `splice-test-token-v2` only. Requires a running LocalNet +Ledger JSON API (`http://localhost:3975`). + +```bash +# 1. Start LocalNet (first run can take 15+ minutes; free host :3000 if nginx conflicts) +npm run localnet:start + +# 2. Fetch the gitignored TestTokenV2 DAR (SHA-verified) and run the smoke +npm run localnet:cip56-transfer + +# 3. Tear down when finished +npm run localnet:teardown +``` + +`localnet:cip56-transfer` fails closed when Ledger is unreachable. The integration test under +`test/integration/localnet/cip56-transfer.test.ts` skips those steps in ordinary `npm test` +(no Docker) and exercises command builders + result parsers only. + +Internal fixtures (CI/scripts only; never published): + +- `fixtures/dar-lifecycle/` — minimal DAR lifecycle sources +- `fixtures/splice-test-token-v2/` — Splice TestTokenV2 DAR (gitignored binary; fetch via npm script) diff --git a/bin/canton-dev-tools b/bin/canton-dev-tools new file mode 100755 index 0000000..e7a43b5 --- /dev/null +++ b/bin/canton-dev-tools @@ -0,0 +1,252 @@ +#!/usr/bin/env bash +set -euo pipefail + +resolve_script_dir() { + local source="$1" + local dir="" + + while [[ -L "${source}" ]]; do + dir="$(cd -P "$(dirname "${source}")" && pwd)" + source="$(readlink "${source}")" + if [[ "${source}" != /* ]]; then + source="${dir}/${source}" + fi + done + + cd -P "$(dirname "${source}")" && pwd +} + +PACKAGE_ROOT="$(cd "$(resolve_script_dir "${BASH_SOURCE[0]}")/.." && pwd)" +LOCALNET_SCRIPT="${PACKAGE_ROOT}/scripts/localnet-cloud.sh" +DEFAULT_QUICKSTART_REPO="https://github.com/digital-asset/cn-quickstart.git" +# Keep in sync with package.json#config (enforced by scripts/check-package-artifacts.ts). +DEFAULT_QUICKSTART_REF="2f4edfc17621a7dfb6d44357050c22f4b3914c89" +DEFAULT_SPLICE_VERSION="0.6.14" +DEFAULT_SCRIBE_VERSION="0.6.14" +DEFAULT_PROTOCOL_VERSION="35" + +log() { + printf '[canton-dev-tools] %s\n' "$*" +} + +usage() { + cat <<'USAGE' +Usage: canton-dev-tools + canton-localnet + +Product commands: + start Start lean LocalNet + Keycloak and wait for ready endpoints + readiness Run endpoint readiness / smoke checks + diagnostics Show LocalNet diagnostic logs + teardown Stop LocalNet services + +Legacy aliases: + setup Prepare LocalNet prerequisites + stop Alias for teardown + logs Alias for diagnostics + status Show docker + endpoint status + smoke Alias for readiness + test Run project integration tests when configured + verify Run setup + start + readiness + test + +One-liners: + npx @fairmint/canton-dev-tools start + npx @fairmint/canton-dev-tools verify + +Environment: + CANTON_LOCALNET_QUICKSTART_DIR Use an existing cn-quickstart/quickstart directory + CANTON_LOCALNET_CACHE_DIR Cache root for fetched cn-quickstart assets + CANTON_LOCALNET_QUICKSTART_REF cn-quickstart git ref (defaults to this package pin) + CANTON_LOCALNET_SPLICE_VERSION Splice image tag (defaults to this package pin) + CANTON_LOCALNET_SCRIBE_VERSION Scribe image tag (defaults to this package pin) + CANTON_LOCALNET_PROTOCOL_VERSION Canton protocol version (defaults to this package pin) + CANTON_LOCALNET_AUTH_MODE oauth2 (package default) or shared-secret + CANTON_LOCALNET_INFRA_ONLY Defaults to true for this package binary +USAGE +} + +require_command() { + local cmd="$1" + if ! command -v "${cmd}" >/dev/null 2>&1; then + log "Missing required command: ${cmd}" + exit 1 + fi +} + +cache_root() { + if [[ -n "${CANTON_LOCALNET_CACHE_DIR:-}" ]]; then + printf '%s' "${CANTON_LOCALNET_CACHE_DIR}" + return + fi + + if [[ -n "${XDG_CACHE_HOME:-}" ]]; then + printf '%s' "${XDG_CACHE_HOME}/fairmint/canton-localnet" + return + fi + + printf '%s' "${HOME}/.cache/fairmint/canton-localnet" +} + +quickstart_ref() { + printf '%s' "${CANTON_LOCALNET_QUICKSTART_REF:-${DEFAULT_QUICKSTART_REF}}" +} + +quickstart_repo() { + printf '%s' "${CANTON_LOCALNET_QUICKSTART_REPO:-${DEFAULT_QUICKSTART_REPO}}" +} + +default_splice_version() { + if [[ -f "${PACKAGE_ROOT}/libs/splice/VERSION" ]]; then + local version="" + version="$(tr -d '[:space:]' < "${PACKAGE_ROOT}/libs/splice/VERSION")" + if [[ -n "${version}" ]]; then + printf '%s' "${version}" + return + fi + fi + + printf '%s' "${DEFAULT_SPLICE_VERSION}" +} + +default_quickstart_root() { + printf '%s/cn-quickstart-%s' "$(cache_root)" "$(quickstart_ref)" +} + +repo_quickstart_dir() { + printf '%s/libs/cn-quickstart/quickstart' "${PACKAGE_ROOT}" +} + +use_repo_quickstart_default() { + [[ -z "${CANTON_LOCALNET_QUICKSTART_DIR:-}" \ + && -f "${PACKAGE_ROOT}/.gitmodules" \ + && ( -d "${PACKAGE_ROOT}/.git" || -f "${PACKAGE_ROOT}/.git" ) ]] +} + +quickstart_dir() { + if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then + printf '%s' "${CANTON_LOCALNET_QUICKSTART_DIR}" + return + fi + + if use_repo_quickstart_default; then + repo_quickstart_dir + return + fi + + printf '%s/quickstart' "$(default_quickstart_root)" +} + +ensure_quickstart_checkout() { + local dir="" + local root="" + local tmp="" + + if use_repo_quickstart_default; then + return + fi + + dir="$(quickstart_dir)" + if [[ -d "${dir}/docker/modules/localnet" ]]; then + return + fi + + if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then + log "CANTON_LOCALNET_QUICKSTART_DIR is not a cn-quickstart quickstart directory: ${dir}" + exit 1 + fi + + require_command git + + root="$(default_quickstart_root)" + if [[ -e "${root}" ]]; then + log "Cached cn-quickstart directory exists but is incomplete: ${root}" + log "Remove it or set CANTON_LOCALNET_CACHE_DIR to use a fresh cache." + exit 1 + fi + + mkdir -p "$(cache_root)" + tmp="$(mktemp -d "$(cache_root)/cn-quickstart.XXXXXX")" + trap 'rm -rf "${tmp}"' RETURN + + log "Fetching cn-quickstart $(quickstart_ref)..." + git -C "${tmp}" init -q + git -C "${tmp}" remote add origin "$(quickstart_repo)" + if git -C "${tmp}" fetch --depth 1 origin "$(quickstart_ref)"; then + git -C "${tmp}" checkout --detach FETCH_HEAD -q + else + log "Direct ref fetch failed; retrying with a blobless repository fetch." + git -C "${tmp}" fetch --filter=blob:none origin + git -C "${tmp}" checkout --detach "$(quickstart_ref)" -q + fi + + if [[ ! -d "${tmp}/quickstart/docker/modules/localnet" ]]; then + log "Fetched cn-quickstart does not contain quickstart/docker/modules/localnet." + return 1 + fi + + mv "${tmp}" "${root}" + trap - RETURN + log "Cached cn-quickstart at ${root}." +} + +normalize_command() { + case "$1" in + readiness) + printf 'smoke' + ;; + diagnostics) + printf 'logs' + ;; + teardown) + printf 'stop' + ;; + *) + printf '%s' "$1" + ;; + esac +} + +main() { + local command="${1:-}" + local normalized="" + local dir="" + + case "${command}" in + "" | -h | --help | help) + usage + if [[ -z "${command}" ]]; then + exit 1 + fi + exit 0 + ;; + start | setup | verify) + ensure_quickstart_checkout + ;; + readiness | diagnostics | teardown | logs | status | stop | smoke | test) + ;; + *) + usage + exit 1 + ;; + esac + + normalized="$(normalize_command "${command}")" + shift || true + + dir="$(quickstart_dir)" + if ! use_repo_quickstart_default; then + export CANTON_LOCALNET_QUICKSTART_DIR="${dir}" + fi + # Apply central pins for npx / direct binary invocations (not only npm scripts). + # Caller env overrides are preserved; package pins are defaults only. + export CANTON_LOCALNET_AUTH_MODE="${CANTON_LOCALNET_AUTH_MODE:-oauth2}" + export CANTON_LOCALNET_QUICKSTART_REF="${CANTON_LOCALNET_QUICKSTART_REF:-${DEFAULT_QUICKSTART_REF}}" + export CANTON_LOCALNET_SPLICE_VERSION="${CANTON_LOCALNET_SPLICE_VERSION:-$(default_splice_version)}" + export CANTON_LOCALNET_SCRIBE_VERSION="${CANTON_LOCALNET_SCRIBE_VERSION:-${DEFAULT_SCRIBE_VERSION}}" + export CANTON_LOCALNET_PROTOCOL_VERSION="${CANTON_LOCALNET_PROTOCOL_VERSION:-${DEFAULT_PROTOCOL_VERSION}}" + export CANTON_LOCALNET_INFRA_ONLY="${CANTON_LOCALNET_INFRA_ONLY:-true}" + + exec bash "${LOCALNET_SCRIPT}" "${normalized}" "$@" +} + +main "$@" diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3ea6d05 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,41 @@ +import typescriptEslintPlugin from '@typescript-eslint/eslint-plugin'; +import typescriptEslintParser from '@typescript-eslint/parser'; +import unusedImportsPlugin from 'eslint-plugin-unused-imports'; + +export default [ + { + ignores: ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**/scripts/**', 'bin/**'], + }, + { + files: ['**/*.ts'], + languageOptions: { + parser: typescriptEslintParser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + plugins: { + '@typescript-eslint': typescriptEslintPlugin, + 'unused-imports': unusedImportsPlugin, + }, + rules: { + 'unused-imports/no-unused-imports': 'warn', + 'unused-imports/no-unused-vars': [ + 'warn', + { + vars: 'all', + varsIgnorePattern: '^_', + args: 'after-used', + argsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + }, + ], + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + }, + }, +]; diff --git a/fixtures/dar-lifecycle/README.md b/fixtures/dar-lifecycle/README.md new file mode 100644 index 0000000..b5a406e --- /dev/null +++ b/fixtures/dar-lifecycle/README.md @@ -0,0 +1,28 @@ +# DAR lifecycle fixture (CI / scripts only) + +This directory is an **internal** DAML fixture used to validate Fairmint DAR lifecycle tooling +(build → upload → vet → breaking-upgrade detection). It is **never published** with +`@fairmint/canton-dev-tools`. + +## Rules + +- Not listed in `package.json#files` +- `scripts/check-package-artifacts.ts` / `npm run pack:check` must fail if any `fixtures/**` or `*.dar` path appears in the pack file list +- Do not import this fixture from published TypeScript exports + +## Layout + +``` +fixtures/dar-lifecycle/ + daml.yaml # minimal package metadata + daml/DarLifecycle/Marker.daml + README.md # this file +``` + +## Building + +Requires `dpm` (Daml Package Manager) on `PATH`. The script fails if `dpm` is missing: + +```bash +npm run fixture:dar-lifecycle:build +``` diff --git a/fixtures/dar-lifecycle/daml.yaml b/fixtures/dar-lifecycle/daml.yaml new file mode 100644 index 0000000..eea36cd --- /dev/null +++ b/fixtures/dar-lifecycle/daml.yaml @@ -0,0 +1,13 @@ +# Minimal internal fixture for DAR lifecycle tooling. +# CI/scripts only — never published with @fairmint/canton-dev-tools. +# +# sdk-version may be bumped when the shared LocalNet / dpm install path is finalized. +sdk-version: 3.4.10 +name: DarLifecycle +source: daml +version: 0.0.1 +dependencies: + - daml-prim + - daml-stdlib +build-options: + - --target=2.1 diff --git a/fixtures/dar-lifecycle/daml/DarLifecycle/Marker.daml b/fixtures/dar-lifecycle/daml/DarLifecycle/Marker.daml new file mode 100644 index 0000000..be95b21 --- /dev/null +++ b/fixtures/dar-lifecycle/daml/DarLifecycle/Marker.daml @@ -0,0 +1,15 @@ +-- | Minimal versionable marker template for DAR lifecycle tooling validation. +-- Intentionally boring: enough to produce a DAR when dpm/daml is available. +module DarLifecycle.Marker where + +template Marker + with + owner : Party + label : Text + where + signatory owner + + choice ArchiveMarker : () + controller owner + do + return () diff --git a/fixtures/splice-test-token-v2/.gitignore b/fixtures/splice-test-token-v2/.gitignore new file mode 100644 index 0000000..c98e744 --- /dev/null +++ b/fixtures/splice-test-token-v2/.gitignore @@ -0,0 +1,2 @@ +# DAR binaries are fetched on demand; never commit them. +*.dar diff --git a/fixtures/splice-test-token-v2/README.md b/fixtures/splice-test-token-v2/README.md new file mode 100644 index 0000000..0b3ae33 --- /dev/null +++ b/fixtures/splice-test-token-v2/README.md @@ -0,0 +1,30 @@ +# Splice `splice-test-token-v2` fixture (CIP-56 / CIP-112) + +Internal reference DAR from +[`canton-network/splice`](https://github.com/canton-network/splice) used for LocalNet CIP-56 +holdings and CIP-112 transfer-instruction smoke tests. + +## Provenance + +| Field | Value | +| ------------- | --------------------------------------------------------------------------- | +| Upstream repo | `https://github.com/canton-network/splice` | +| Ref | `package.json#config.localnet_splice_version` (currently `0.6.14`) | +| Upstream path | `daml/dars/splice-test-token-v2-1.0.0.dar` | +| Local path | `fixtures/splice-test-token-v2/splice-test-token-v2-1.0.0.dar` (gitignored) | +| SHA-256 | `43fcf2fcf4e84861501a0c00e8550e2863e1aad553b1fb772ee8aa7bca7fd245` | +| Package id | `a38a96b6f46c14c599b2763bc4fc68911a9cada90f89c599a1401e8e3df685e1` | + +## Rules + +- Not listed in `package.json#files` +- `npm run pack:check` / `scripts/check-package-artifacts.ts` must fail if any `fixtures/**` or `*.dar` path appears in the pack file list +- Do not import the DAR binary from published TypeScript exports (helpers may export hash / package-id constants only) + +## Fetch + +```bash +npm run fixture:splice-test-token-v2:fetch +``` + +Sparse-clones the pinned Splice ref, copies the DAR, and asserts the SHA-256 above. diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..39d0ec3 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,16 @@ +/** @type {import('jest').Config} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/test'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + testMatch: ['**/test/unit/**/*.test.ts'], + testPathIgnorePatterns: ['/node_modules/'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], + transform: { + '^.+\\.(ts|tsx)$': ['ts-jest', { diagnostics: false }], + }, + testTimeout: 10000, +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9a64921 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,9060 @@ +{ + "name": "@fairmint/canton-dev-tools", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@fairmint/canton-dev-tools", + "version": "0.1.0", + "license": "MIT", + "bin": { + "canton-dev-tools": "bin/canton-dev-tools", + "canton-localnet": "bin/canton-dev-tools" + }, + "devDependencies": { + "@fairmint/canton-node-sdk": "0.0.251", + "@types/jest": "30.0.0", + "@types/node": "22.15.29", + "@typescript-eslint/eslint-plugin": "8.62.1", + "@typescript-eslint/parser": "8.62.1", + "eslint": "9.39.2", + "eslint-plugin-unused-imports": "4.4.1", + "jest": "30.4.2", + "prettier": "3.8.4", + "ts-jest": "29.4.11", + "tsx": "4.23.0", + "typescript": "5.9.3" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@fairmint/canton-node-sdk": ">=0.0.232 <0.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-acs-reader": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-acs-reader/-/core-acs-reader-1.18.0.tgz", + "integrity": "sha512-ZWSK2wiiaMvzVtEWK9kchQ/zuYGK0Ng4aJMjgpJzoGdkpXze1z0TIM7cmxJGqbZTZTYv0z0pXdaxQdTUU9IESQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-provider-ledger": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "bignumber.js": "^9.3.1", + "dayjs": "^1.11.21", + "openapi-fetch": "^0.15.2", + "pino": "^10.3.1", + "typescript-lru-cache": "^2.0.0" + } + }, + "node_modules/@canton-network/core-acs-reader/node_modules/openapi-fetch": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.15.2.tgz", + "integrity": "sha512-rdYTzUmSsJevmNqg7fwUVGuKc2Gfb9h6ph74EVPkPfIGJaZTfqdIbJahtbJ3qg1LKinln30hqZniLnKpH0RJBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.0.15" + } + }, + "node_modules/@canton-network/core-acs-reader/node_modules/openapi-typescript-helpers": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz", + "integrity": "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-amulet-service": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-amulet-service/-/core-amulet-service-1.10.0.tgz", + "integrity": "sha512-L+6Rw0dc/izu734weYa7isOGrqZfQRm+JQ4fNuaAf+2JWsNNxVM7T3c3UBNO1k1QGF+G+DPDiG2ueqS4P2/X7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client": "^1.10.0", + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-splice-client": "^1.10.0", + "@canton-network/core-token-standard-service": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "bignumber.js": "^10.0.2", + "dayjs": "^1.11.21", + "decimal.js": "^10.6.0", + "openapi-fetch": "^0.17.0", + "pino": "^10.3.1" + } + }, + "node_modules/@canton-network/core-amulet-service/node_modules/bignumber.js": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-asyncapi-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-asyncapi-client/-/core-asyncapi-client-1.10.0.tgz", + "integrity": "sha512-3xDjiv9tSRpI5utUrT8GUUIxQmdpnLUo7qTDvL6l0Lf2AgUBrAykILf3GWXqvoahHKEQBQeNd2m2wLhBrsjWYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "bignumber.js": "^10.0.2", + "pino": "^10.3.1" + } + }, + "node_modules/@canton-network/core-asyncapi-client/node_modules/bignumber.js": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-ledger-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-ledger-client/-/core-ledger-client-1.10.0.tgz", + "integrity": "sha512-uGjC2krZs5t5vZNE7IktELuwTIQm7+KnpG8d4gqKfQFaGZyd/YhcMX6VANxuatNp3WiaJs9nSI6un0HqiBg4Bg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-ledger-proto": "^1.9.0", + "@canton-network/core-splice-client": "^1.10.0", + "@canton-network/core-token-standard": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "bignumber.js": "^10.0.2", + "dayjs": "^1.11.21", + "openapi-fetch": "^0.17.0", + "pino": "^10.3.1", + "typescript-lru-cache": "^2.0.0" + } + }, + "node_modules/@canton-network/core-ledger-client-types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-ledger-client-types/-/core-ledger-client-types-1.10.0.tgz", + "integrity": "sha512-8BDqz83WqMSegebWez/RsTrSyOsb1Z487a0qyzrWX9snuxE12UdM+hPeu71hEnL8jG4WzE0SJK7doo947qe8ZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-types": "^1.10.0", + "bignumber.js": "^10.0.2", + "dayjs": "^1.11.21", + "openapi-fetch": "^0.17.0", + "pino": "^10.3.1" + } + }, + "node_modules/@canton-network/core-ledger-client-types/node_modules/bignumber.js": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-ledger-client/node_modules/bignumber.js": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-ledger-proto": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-ledger-proto/-/core-ledger-proto-1.9.0.tgz", + "integrity": "sha512-9AsDgfpJYtydjVnfzemvZzRmxaogNcPTPnpwJRk2fHJJ/Eopsz+kYJbrPYhuXS2yLHBPRxn0za9JfazWiO2MxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1" + } + }, + "node_modules/@canton-network/core-provider-dapp": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-provider-dapp/-/core-provider-dapp-1.10.0.tgz", + "integrity": "sha512-t5zXot2cAitS8wx2cYOuCPXHo2amjoqjCizpDUV5o4ltJsYfU+BblcDRKBI0SStNxQHKBihsScGJasNZ67YQHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-rpc-transport": "^1.10.0", + "@canton-network/core-splice-provider": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-dapp-remote-rpc-client": "^1.10.0", + "@canton-network/core-wallet-dapp-rpc-client": "^1.10.0" + } + }, + "node_modules/@canton-network/core-provider-ledger": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-provider-ledger/-/core-provider-ledger-1.10.0.tgz", + "integrity": "sha512-FGpp0bnWVwjMl5JlxuafzMoBH/XArjlIJ1w19axLjgJCZZvlfw489K4a0V+lXUILRw39hhcmVh9u/nom8ouC3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client": "^1.10.0", + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-splice-provider": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "pino": "^10.3.1" + } + }, + "node_modules/@canton-network/core-rpc-errors": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-rpc-errors/-/core-rpc-errors-1.9.0.tgz", + "integrity": "sha512-u7cbt/opgHnvCRRTG3xe+PrskYtdVxDcnJOWYK6h8uQ6QdzlhwYMEe/UGMRcqMyuNnuEqwgkzPR4wCsXaxN6OA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@metamask/rpc-errors": "^7.0.3" + } + }, + "node_modules/@canton-network/core-rpc-transport": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-rpc-transport/-/core-rpc-transport-1.10.0.tgz", + "integrity": "sha512-hR98Kz8QlQ7ATwk6mrYsxQXwo0H+qepmQ3XxyxUPW+oh7ijzh5YKdQ4+rAozKxMZ4jXQ1TqrYRbDh+WNYpuhIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-types": "^1.10.0", + "uuid": "^14.0.1" + } + }, + "node_modules/@canton-network/core-signing-lib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-signing-lib/-/core-signing-lib-1.10.0.tgz", + "integrity": "sha512-KsM1Hk7GI+BclKX38t62QFIcvv8HPkcPvLE1HvahFHcCXRRk9fndfcZex46PUs6j/s7kjcp9QRJpB+qZNUsqCA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-wallet-auth": "^1.10.0", + "fs-extra": "^11.3.6", + "lodash": "^4.18.1", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1", + "zod": "^4.4.3" + } + }, + "node_modules/@canton-network/core-splice-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-splice-client/-/core-splice-client-1.10.0.tgz", + "integrity": "sha512-ESdJ03qCB6Qg3tBTfOuVUtIvs9GsbWZFYQcXW4yRjgTmQUzreUbmIITy4V6rgJ6Z1la/GTIjNHbunV1nV7YJaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "openapi-fetch": "^0.17.0", + "uuid": "^14.0.1", + "zod": "^4.4.3" + } + }, + "node_modules/@canton-network/core-splice-provider": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-splice-provider/-/core-splice-provider-1.10.0.tgz", + "integrity": "sha512-GVDiweHPSWJ4PIC+OerP3EAxTQlen580e9XztGU8zdccGhqp0YJiJH4rpxbzEZEJfDRKnw8pTArg1C9ij1xjgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-rpc-transport": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-dapp-remote-rpc-client": "^1.10.0", + "@canton-network/core-wallet-dapp-rpc-client": "^1.10.0" + } + }, + "node_modules/@canton-network/core-token-standard": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-token-standard/-/core-token-standard-1.10.0.tgz", + "integrity": "sha512-5NYLD6h/ofcIhykjgkREqAOdyiX98+BpM3ynv62d0BiCXYoRuARkb8O7T37d2I+knJcQgpjTOxV+OwsR/ER8tw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "@daml/types": "^3.5.2", + "@mojotech/json-type-validation": "^3.1.0", + "lodash": "^4.18.1", + "openapi-fetch": "^0.17.0", + "uuid": "^14.0.1", + "zod": "^4.4.3" + } + }, + "node_modules/@canton-network/core-token-standard-service": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-token-standard-service/-/core-token-standard-service-1.10.0.tgz", + "integrity": "sha512-zPcE5i8zP8dcL+kH7bGsdcWp/ctTy2zeF89/nvE0hIzG5DoKTxQ7oAy5jsd0Dg3PeHWYGGGrHUDk5aCvXEUzTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-acs-reader": "^1.18.0", + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-provider-ledger": "^1.10.0", + "@canton-network/core-splice-client": "^1.10.0", + "@canton-network/core-token-standard": "^1.10.0", + "@canton-network/core-tx-parser": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "dayjs": "^1.11.21", + "decimal.js": "^10.6.0", + "openapi-fetch": "^0.17.0", + "pino": "^10.3.1", + "typescript-lru-cache": "^2.0.0" + } + }, + "node_modules/@canton-network/core-tx-parser": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-tx-parser/-/core-tx-parser-1.10.0.tgz", + "integrity": "sha512-23tSqTPFUqr9ZNy6kwy4L1F3AzKGdMIEWUOoCWqk96UrKQlbAXYfeMzv3+++EXW7rOcp9BAXp1//I4kLevkTBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-client-types": "^1.10.0", + "@canton-network/core-ledger-proto": "^1.9.0", + "@canton-network/core-provider-ledger": "^1.10.0", + "@canton-network/core-splice-client": "^1.10.0", + "@canton-network/core-token-standard": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "@canton-network/core-wallet-auth": "^1.10.0", + "bignumber.js": "^10.0.2", + "dayjs": "^1.11.21", + "openapi-fetch": "^0.17.0", + "pino": "^10.3.1" + } + }, + "node_modules/@canton-network/core-tx-parser/node_modules/bignumber.js": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-tx-visualizer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-tx-visualizer/-/core-tx-visualizer-1.9.0.tgz", + "integrity": "sha512-zazjzBYwuxa3fT7zhq/w5wCo8KGwMsKIrCe3zlp4fr9UYPHnKeSpc+G8vzO+51UEOjBYVJu9DdNlu6l64JVFPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-ledger-proto": "^1.9.0", + "@types/node": "^25.9.4", + "camelcase-keys": "^10.0.2" + } + }, + "node_modules/@canton-network/core-tx-visualizer/node_modules/@types/node": { + "version": "25.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.5.tgz", + "integrity": "sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@canton-network/core-tx-visualizer/node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@canton-network/core-types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-types/-/core-types-1.10.0.tgz", + "integrity": "sha512-MFP0iY8REKtq4kM85Uxuq80V3KZ6BncXw8kGfcD0Q4rroheZ+fXCY7Tbzz5Zohd/Jv4ztJsC7UGxMKijcmp4IA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@daml/types": "^3.5.2", + "uuid": "^14.0.1", + "zod": "^4.4.3" + } + }, + "node_modules/@canton-network/core-wallet-auth": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-wallet-auth/-/core-wallet-auth-1.10.0.tgz", + "integrity": "sha512-nHalOp7ucrsCdB893GL6iydP4fTDUxaUo0txnDWWRw50c8u3pALetTGD6SG89W/b/U1xsa1uxelGMpLiUSd2Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-rpc-errors": "^1.9.0", + "@canton-network/core-types": "^1.10.0", + "jose": "^6.2.3", + "zod": "^4.4.3" + } + }, + "node_modules/@canton-network/core-wallet-dapp-remote-rpc-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-wallet-dapp-remote-rpc-client/-/core-wallet-dapp-remote-rpc-client-1.10.0.tgz", + "integrity": "sha512-KqqxI42BEjFkqCB7r0d2rutuMp4znxflwxDu8Ea6KUZaT1BGpXXfbVtgHPfqymBxfAFJK2TgWNQncBp4PiRhOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-rpc-transport": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "lodash": "^4.18.1" + } + }, + "node_modules/@canton-network/core-wallet-dapp-rpc-client": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@canton-network/core-wallet-dapp-rpc-client/-/core-wallet-dapp-rpc-client-1.10.0.tgz", + "integrity": "sha512-adAYmLlrwwJDFOPlS2Av12toGPhXW6kTmZsT7G+Q1sr0owdUlyMB7PBeXmRShvxGQpiz/qWalVyfVRAyyRsUEg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-rpc-transport": "^1.10.0", + "@canton-network/core-types": "^1.10.0", + "lodash": "^4.18.1" + } + }, + "node_modules/@canton-network/wallet-sdk": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@canton-network/wallet-sdk/-/wallet-sdk-1.3.1.tgz", + "integrity": "sha512-vf/n0TiXt98+KzoAp7OML9En4wrLf5Dasrkjw09z/QVcVVHJP8gkVnpnBlTKAIAlY29sxo4PqeJ7LIc4PT/vEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@canton-network/core-acs-reader": "^1.11.1", + "@canton-network/core-amulet-service": "^1.3.1", + "@canton-network/core-asyncapi-client": "^1.3.1", + "@canton-network/core-ledger-client-types": "^1.3.1", + "@canton-network/core-ledger-proto": "^1.3.1", + "@canton-network/core-provider-dapp": "^1.3.1", + "@canton-network/core-provider-ledger": "^1.3.1", + "@canton-network/core-signing-lib": "^1.3.1", + "@canton-network/core-splice-client": "^1.3.1", + "@canton-network/core-splice-provider": "^1.3.1", + "@canton-network/core-token-standard": "^1.3.1", + "@canton-network/core-token-standard-service": "^1.3.1", + "@canton-network/core-tx-parser": "^1.3.1", + "@canton-network/core-tx-visualizer": "^1.3.1", + "@canton-network/core-types": "^1.3.1", + "@canton-network/core-wallet-auth": "^1.3.1", + "@grpc/grpc-js": "^1.14.3", + "@protobuf-ts/grpc-transport": "^2.11.1", + "decimal.js": "^10.6.0", + "jose": "^6.1.3", + "pino": "^10.3.1", + "pino-pretty": "^13.1.3", + "typescript-lru-cache": "^2.0.0", + "uuid": "^14.0.0" + } + }, + "node_modules/@daml/types": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@daml/types/-/types-3.5.2.tgz", + "integrity": "sha512-lWAXNOl1tdzodvDDw7XTv6dUZA0MeAQZtTklh+5kegNt1asKAhreppYkD+bogYU1DnMXUTeMgGmmMBZt1gZpJg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mojotech/json-type-validation": "^3.1.0", + "@types/lodash": "^4.5", + "lodash": "^4.5" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@fairmint/canton-node-sdk": { + "version": "0.0.251", + "resolved": "https://registry.npmjs.org/@fairmint/canton-node-sdk/-/canton-node-sdk-0.0.251.tgz", + "integrity": "sha512-+jLk21p0Jr6EaKoDrkpAEi5tvGUvUf/xH6WEUbPz5dzVaQ6xE86xA2is0StVeRPYNhQL/9/mbB28+tUsS9jQxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@canton-network/wallet-sdk": "1.3.1", + "@hardlydifficult/rest-client": "1.0.65", + "@hardlydifficult/websocket": "1.0.73", + "@stellar/stellar-base": "15.0.0", + "axios": "1.18.1", + "dotenv": "17.4.2", + "glob": "13.0.6", + "openapi-fetch": "0.17.0", + "openapi-typescript": "7.13.0", + "pino": "10.3.1", + "ws": "8.21.0", + "zod": "4.4.3" + }, + "bin": { + "canton-localnet": "bin/canton-localnet" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hardlydifficult/date-time": { + "version": "1.0.27", + "resolved": "https://registry.npmjs.org/@hardlydifficult/date-time/-/date-time-1.0.27.tgz", + "integrity": "sha512-tNOrlq+Q4YKWZyWko+XfGWTWf9oW8gMqJUKxG68HfPmfwMj+JWHYOBC9yXrKGKBnZ2IhXtWemVuLoB3knmwsQg==", + "dev": true, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@hardlydifficult/rest-client": { + "version": "1.0.65", + "resolved": "https://registry.npmjs.org/@hardlydifficult/rest-client/-/rest-client-1.0.65.tgz", + "integrity": "sha512-Fxjz3VTgkj0kiUe76YsOqMb2zvOCqIOBF1M8QfbFDkK7+iGY4rjcQJqsCEsgX11K7RgX3fRwWq3hmQfdk3P7aQ==", + "dev": true, + "dependencies": { + "@hardlydifficult/date-time": "1.0.27", + "axios": "1.13.6" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "zod": ">=3.0.0" + } + }, + "node_modules/@hardlydifficult/rest-client/node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@hardlydifficult/rest-client/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@hardlydifficult/websocket": { + "version": "1.0.73", + "resolved": "https://registry.npmjs.org/@hardlydifficult/websocket/-/websocket-1.0.73.tgz", + "integrity": "sha512-h9l5kr3ZWGEnrHOfBil1jpMI+k721A/+KlW6vLnGiaOWIh8+q2TayXZS8swrt7mPhapf161dFCjCgJ0qwHlkIw==", + "dev": true, + "dependencies": { + "@hardlydifficult/date-time": "1.0.27", + "ws": "8.19.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@hardlydifficult/websocket/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@metamask/rpc-errors": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-7.0.3.tgz", + "integrity": "sha512-nrEaeBawm8yFU7hetJKok/CUs0tQsWtTqp3OLbFhPUMXYqU7uI5LAV5vi9o7rTjFkUyof7Nzbw5bea5+1ou+dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@metamask/utils": "^11.4.2", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": "^18.20 || ^20.17 || >=22" + } + }, + "node_modules/@metamask/superstruct": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.4.1.tgz", + "integrity": "sha512-caTaaBUcwBGbUNf3r0uT48upX4nECRbKhQ9pPOfW4sIkfcIUUDV4S9DZxq/5fuNPVt5KWpyd5xIIz0sP+iWLlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/utils": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-11.11.0.tgz", + "integrity": "sha512-0nF2CWjWQr/m0Y2t2lJnBTU1/CZPPTvKvcESLplyWe/tyeb8zFOi/FeneDmaFnML6LYRIGZU6f+xR0jKAIUZfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "@types/lodash": "^4.17.20", + "debug": "^4.3.4", + "lodash": "^4.17.21", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": "^18.18 || ^20.14 || >=22" + } + }, + "node_modules/@metamask/utils/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@mojotech/json-type-validation": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mojotech/json-type-validation/-/json-type-validation-3.1.0.tgz", + "integrity": "sha512-ThH2EbHEUCPMHhXAtmYcDi0gmV+PZak4uvuWBMiBDqUuz7gGQUrsE5o1J6kKNLDX5cXAPqsfJ7uTfTcNdCDXxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@protobuf-ts/grpc-transport": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/grpc-transport/-/grpc-transport-2.11.1.tgz", + "integrity": "sha512-l6wrcFffY+tuNnuyrNCkRM8hDIsAZVLA8Mn7PKdVyYxITosYh60qW663p9kL6TWXYuDCL3oxH8ih3vLKTDyhtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1" + }, + "peerDependencies": { + "@grpc/grpc-js": "^1.6.0" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz", + "integrity": "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz", + "integrity": "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.19", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.19.tgz", + "integrity": "sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.3.1", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@redocly/openapi-core/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@stellar/js-xdr": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-4.0.0.tgz", + "integrity": "sha512-+NmNa7Tk5BI5XFdy/6xGTqAN4J9a9KgCrCGhj2uEUTCBhLkch0M+QbKzNH8zEnejWe0p8w+0q5hUVX6L3OzoVA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20.0.0", + "pnpm": ">=9.0.0" + } + }, + "node_modules/@stellar/stellar-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-15.0.0.tgz", + "integrity": "sha512-XQhxUr9BYiEcFcgc4oWcCMR9QJCny/GmmGsuwPKf/ieIcOeb5149KLHYx9mJCA0ea8QbucR2/GzV58QbXOTxQA==", + "deprecated": "This package is now rolled into @stellar/stellar-sdk. Please use @stellar/stellar-sdk to continue receiving updates and support.", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.9.7", + "@stellar/js-xdr": "^4.0.0", + "base32.js": "^0.1.0", + "bignumber.js": "^9.3.1", + "buffer": "^6.0.3", + "sha.js": "^2.4.12" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", + "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz", + "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/type-utils": "8.62.1", + "@typescript-eslint/utils": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.62.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz", + "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz", + "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.62.1", + "@typescript-eslint/types": "^8.62.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz", + "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz", + "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz", + "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1", + "@typescript-eslint/utils": "8.62.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz", + "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz", + "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.62.1", + "@typescript-eslint/tsconfig-utils": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz", + "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz", + "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.62.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz", + "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-9.0.0.tgz", + "integrity": "sha512-TO9xmyXTZ9HUHI8M1OnvExxYB0eYVS/1e5s7IDMTAoIcwUd+aNcFODs6Xk83mobk0velyHFQgA1yIrvYc6wclw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-10.0.2.tgz", + "integrity": "sha512-PVHCLVbJ7nWGal0lPAmBN5eSLjIynlMUk2EPmL9aPl6QyJ6+FoszTKwldPzkuVqg5teZbPTbb8Oenzyw9GSJRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^9.0.0", + "map-obj": "6.0.0", + "quick-lru": "^7.3.0", + "type-fest": "^5.4.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.403", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.403.tgz", + "integrity": "sha512-MQsYmdaLzvaCX5j+ZZBr5Fm6uCCnPQcRtlvmvRlWqrXy+BH2O4ffXIAScF+JQznQWB9brWp4lSD9Z4yNmaf2BA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", + "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-copy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.4.tgz", + "integrity": "sha512-eVAiWVNPSEGIzDl5yPuLrx8fNMogScXvD9xp1Kzd41FjRIz2I3sSIcxsFeM5EzFfHAfobdvs8ZySffUopljvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jose": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.8.tgz", + "integrity": "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-6.0.0.tgz", + "integrity": "sha512-PwDvwt/tK70+luLw5k9ySLtzLAzwf7tZTY9GBj63Y010nHRPjwHcQTpTd5JwQqITC2ty7prtxBo71iwyYY0TAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-fetch": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.17.0.tgz", + "integrity": "sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.1.0" + } + }, + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/openapi-typescript-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.1.0.tgz", + "integrity": "sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/openapi-typescript/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^4.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pino-pretty/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "dev": true, + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-warning": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true, + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-7.3.0.tgz", + "integrity": "sha512-k9lSsjl36EJdK7I06v7APZCbyGT2vMTsYSRX1Q2nbYmnkBqgUhRkAuzH08Ciotteu/PLJmIF2+tti7o3C/ts2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/thread-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz", + "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "real-require": "^1.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/thread-stream/node_modules/real-require": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz", + "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-jest": { + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.0", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.0.tgz", + "integrity": "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-lru-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz", + "integrity": "sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1f335e4 --- /dev/null +++ b/package.json @@ -0,0 +1,98 @@ +{ + "name": "@fairmint/canton-dev-tools", + "version": "0.1.0", + "description": "Shared LocalNet CLI and Canton integration-test helpers for Fairmint", + "keywords": [ + "canton", + "localnet", + "testing", + "fairmint" + ], + "homepage": "https://github.com/Fairmint/canton-dev-tools#readme", + "bugs": { + "url": "https://github.com/Fairmint/canton-dev-tools/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Fairmint/canton-dev-tools.git" + }, + "license": "MIT", + "author": "Fairmint", + "config": { + "localnet_protocol_version": "35", + "localnet_quickstart_ref": "2f4edfc17621a7dfb6d44357050c22f4b3914c89", + "localnet_scribe_version": "0.6.14", + "localnet_splice_version": "0.6.14" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "bin": { + "canton-dev-tools": "bin/canton-dev-tools", + "canton-localnet": "bin/canton-dev-tools" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./testing": { + "types": "./dist/testing/index.d.ts", + "default": "./dist/testing/index.js" + } + }, + "files": [ + "bin/canton-dev-tools", + "scripts/localnet-cloud.sh", + "dist/**" + ], + "scripts": { + "build": "tsc", + "check:package-artifacts": "tsx scripts/check-package-artifacts.ts", + "clean": "rm -rf dist coverage", + "fix": "npm run lint:fix && npm run format:fix", + "format": "prettier --check .", + "format:fix": "prettier --write .", + "lint": "eslint . --max-warnings=0", + "lint:fix": "eslint . --fix --max-warnings=0", + "localnet": "bash ./bin/canton-dev-tools", + "localnet:diagnostics": "npm run -s localnet -- diagnostics", + "localnet:readiness": "npm run -s localnet -- readiness", + "localnet:setup": "npm run -s localnet -- setup", + "localnet:start": "npm run -s localnet -- start", + "localnet:teardown": "npm run -s localnet -- teardown", + "fixture:dar-lifecycle:build": "bash scripts/build-dar-lifecycle-fixture.sh", + "fixture:splice-test-token-v2:fetch": "tsx scripts/fetch-splice-test-token-v2.ts", + "localnet:cip56-transfer": "CANTON_CIP56_REQUIRE_LOCALNET=1 tsx scripts/run-cip56-transfer-smoke.ts", + "pack:check": "npm run check:package-artifacts", + "prepack": "npm run clean && npm run build", + "prepare-release": "tsx scripts/prepare-release.ts", + "prepublishOnly": "npm run prepack", + "test": "npm run -s typecheck && jest", + "test:ci": "npm run -s typecheck && jest --runInBand", + "test:watch": "jest --watch", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "@fairmint/canton-node-sdk": ">=0.0.232 <0.1.0" + }, + "devDependencies": { + "@fairmint/canton-node-sdk": "0.0.251", + "@types/jest": "30.0.0", + "@types/node": "22.15.29", + "@typescript-eslint/eslint-plugin": "8.62.1", + "@typescript-eslint/parser": "8.62.1", + "eslint": "9.39.2", + "eslint-plugin-unused-imports": "4.4.1", + "jest": "30.4.2", + "prettier": "3.8.4", + "ts-jest": "29.4.11", + "tsx": "4.23.0", + "typescript": "5.9.3" + }, + "engines": { + "node": ">=22" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/scripts/build-dar-lifecycle-fixture.sh b/scripts/build-dar-lifecycle-fixture.sh new file mode 100755 index 0000000..fcea868 --- /dev/null +++ b/scripts/build-dar-lifecycle-fixture.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Build the internal DAR lifecycle fixture. Fails closed if dpm is missing. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +FIXTURE_DIR="${ROOT}/fixtures/dar-lifecycle" + +if ! command -v dpm >/dev/null 2>&1; then + printf '[fixture:dar-lifecycle] dpm not found on PATH; install the Daml Package Manager and retry.\n' >&2 + exit 1 +fi + +if [[ ! -f "${FIXTURE_DIR}/daml.yaml" ]]; then + printf '[fixture:dar-lifecycle] missing daml.yaml at %s\n' "${FIXTURE_DIR}" >&2 + exit 1 +fi + +printf '[fixture:dar-lifecycle] building DAR with dpm...\n' +( + cd "${FIXTURE_DIR}" + dpm build +) + +printf '[fixture:dar-lifecycle] build complete.\n' diff --git a/scripts/check-package-artifacts.ts b/scripts/check-package-artifacts.ts new file mode 100644 index 0000000..307cc03 --- /dev/null +++ b/scripts/check-package-artifacts.ts @@ -0,0 +1,240 @@ +#!/usr/bin/env tsx + +import { spawnSync, type SpawnSyncReturns } from 'child_process'; +import { chmodSync, cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +interface NpmPackFile { + path: string; + size: number; + mode: number; +} + +interface NpmPackResult { + id: string; + name: string; + version: string; + size: number; + unpackedSize: number; + filename: string; + files: NpmPackFile[]; +} + +const DEFAULT_MAX_UNPACKED_BYTES = 5 * 1024 * 1024; +const configuredMaxUnpackedBytes = process.env['MAX_PACKAGE_UNPACKED_BYTES']; +const maxUnpackedBytes = parseMaxUnpackedBytes(configuredMaxUnpackedBytes); + +function formatBytes(bytes: number): string { + return `${(bytes / 1024 / 1024).toFixed(2)} MB`; +} + +function parseMaxUnpackedBytes(rawValue: string | undefined): number { + const parsedValue = rawValue === undefined ? DEFAULT_MAX_UNPACKED_BYTES : Number(rawValue); + + if (!Number.isInteger(parsedValue) || parsedValue <= 0) { + throw new Error( + `MAX_PACKAGE_UNPACKED_BYTES must be a positive integer in bytes, got ${JSON.stringify(rawValue)}` + ); + } + + return parsedValue; +} + +function forbiddenPackagePathReason(packagePath: string): string | null { + if (packagePath.endsWith('.dar')) return 'DAML DAR files are not runtime package artifacts'; + if (packagePath === 'fixtures' || packagePath.startsWith('fixtures/')) { + return 'internal fixtures (including DAR lifecycle sources) must not be published'; + } + if (packagePath === 'internal' || packagePath.startsWith('internal/')) { + return 'internal CI-only assets must not be published'; + } + if (packagePath === 'libs' || packagePath.startsWith('libs/')) { + return 'submodules under libs/ must not be published'; + } + if (packagePath === 'node_modules' || packagePath.startsWith('node_modules/')) { + return 'node_modules must not be published'; + } + if (packagePath === 'core' || packagePath.startsWith('core.')) { + return 'crash dumps must not be published'; + } + return null; +} + +function parsePackResults(stdout: string): NpmPackResult[] { + try { + return JSON.parse(stdout) as NpmPackResult[]; + } catch (error) { + throw new Error( + `npm pack did not return valid JSON: ${error instanceof Error ? error.message : String(error)}` + ); + } +} + +function spawnFailureDetails(command: string, result: SpawnSyncReturns): string { + const details = [`status ${result.status ?? 'unknown'}`]; + + if (result.signal) { + details.push(`signal ${result.signal}`); + } + if (result.error) { + details.push(`error ${result.error.message}`); + } + + return `${command} failed (${details.join(', ')})`; +} + +function throwIfSpawnFailed(command: string, result: SpawnSyncReturns): void { + if (result.status === 0 && !result.signal && !result.error) { + return; + } + + if (result.stderr) { + console.error(result.stderr); + } + if (result.stdout) { + console.error(result.stdout); + } + + throw new Error(spawnFailureDetails(command, result)); +} + +function readPackageConfigPins(): { + quickstartRef: string; + spliceVersion: string; + scribeVersion: string; + protocolVersion: string; +} { + const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as { + config?: { + localnet_quickstart_ref?: string; + localnet_splice_version?: string; + localnet_scribe_version?: string; + localnet_protocol_version?: string; + }; + }; + + const quickstartRef = pkg.config?.localnet_quickstart_ref; + const spliceVersion = pkg.config?.localnet_splice_version; + const scribeVersion = pkg.config?.localnet_scribe_version; + const protocolVersion = pkg.config?.localnet_protocol_version; + if (!quickstartRef || !spliceVersion || !scribeVersion || !protocolVersion) { + throw new Error( + 'package.json config must define localnet_quickstart_ref, localnet_splice_version, localnet_scribe_version, and localnet_protocol_version' + ); + } + + return { quickstartRef, spliceVersion, scribeVersion, protocolVersion }; +} + +function verifyPackagedLocalnetPins(): void { + const localnetBin = readFileSync(join(process.cwd(), 'bin', 'canton-dev-tools'), 'utf8'); + const { quickstartRef, spliceVersion, scribeVersion, protocolVersion } = readPackageConfigPins(); + + const expectedDefaults: Array<[string, string]> = [ + ['DEFAULT_QUICKSTART_REF', quickstartRef], + ['DEFAULT_SPLICE_VERSION', spliceVersion], + ['DEFAULT_SCRIBE_VERSION', scribeVersion], + ['DEFAULT_PROTOCOL_VERSION', protocolVersion], + ]; + + for (const [name, value] of expectedDefaults) { + if (!localnetBin.includes(`${name}="${value}"`)) { + throw new Error( + `bin/canton-dev-tools must hardcode ${name}="${value}" for npx pin ownership` + ); + } + } +} + +function verifyPackagedLocalnetBinary(): void { + const tempDir = mkdtempSync(join(tmpdir(), 'canton-dev-tools-package-')); + + try { + const packageRoot = join(tempDir, 'node_modules', '@fairmint', 'canton-dev-tools'); + const binDir = join(tempDir, 'node_modules', '.bin'); + const localnetBin = join(packageRoot, 'bin', 'canton-dev-tools'); + const localnetSymlink = join(binDir, 'canton-localnet'); + + mkdirSync(packageRoot, { recursive: true }); + mkdirSync(binDir, { recursive: true }); + cpSync(join(process.cwd(), 'bin'), join(packageRoot, 'bin'), { recursive: true }); + cpSync(join(process.cwd(), 'scripts'), join(packageRoot, 'scripts'), { recursive: true }); + chmodSync(localnetBin, 0o755); + symlinkSync('../@fairmint/canton-dev-tools/bin/canton-dev-tools', localnetSymlink); + + const logs = spawnSync(localnetSymlink, ['diagnostics'], { + cwd: tempDir, + encoding: 'utf8', + env: { + ...process.env, + CANTON_LOCALNET_CACHE_DIR: join(tempDir, 'cache'), + HOME: join(tempDir, 'home'), + }, + }); + throwIfSpawnFailed('packaged canton-localnet diagnostics', logs); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } +} + +verifyPackagedLocalnetPins(); + +const prepack = spawnSync('npm', ['run', 'prepack'], { + encoding: 'utf8', +}); +throwIfSpawnFailed('npm run prepack', prepack); + +const packArgs = ['pack', '--dry-run', '--json', '--ignore-scripts']; +const pack = spawnSync('npm', packArgs, { + encoding: 'utf8', +}); +throwIfSpawnFailed(`npm ${packArgs.join(' ')}`, pack); + +const [result] = parsePackResults(pack.stdout); +if (!result) { + throw new Error('npm pack returned no package metadata'); +} + +const errors: string[] = []; + +if (result.unpackedSize > maxUnpackedBytes) { + errors.push( + `package unpacked size ${formatBytes(result.unpackedSize)} exceeds limit ${formatBytes(maxUnpackedBytes)}` + ); +} + +const packagePaths = new Set(result.files.map((file) => file.path)); +for (const requiredPath of [ + 'dist/index.js', + 'dist/index.d.ts', + 'dist/testing/index.js', + 'dist/testing/index.d.ts', + 'bin/canton-dev-tools', + 'scripts/localnet-cloud.sh', +]) { + if (!packagePaths.has(requiredPath)) { + errors.push(`package is missing required runtime entry ${requiredPath}`); + } +} + +for (const file of result.files) { + const reason = forbiddenPackagePathReason(file.path); + if (reason) { + errors.push(`${file.path}: ${reason}`); + } +} + +if (errors.length > 0) { + console.error(`\n${result.name}@${result.version} package artifact check failed:\n`); + for (const error of errors) { + console.error(` ✗ ${error}`); + } + process.exit(1); +} + +verifyPackagedLocalnetBinary(); + +console.log( + `✓ ${result.name}@${result.version} package artifact is ${formatBytes(result.unpackedSize)} unpacked across ${result.files.length} files` +); diff --git a/scripts/fetch-splice-test-token-v2.ts b/scripts/fetch-splice-test-token-v2.ts new file mode 100644 index 0000000..35cb8cc --- /dev/null +++ b/scripts/fetch-splice-test-token-v2.ts @@ -0,0 +1,116 @@ +#!/usr/bin/env node + +/** + * Sparse-fetch Splice `splice-test-token-v2-1.0.0.dar` into fixtures/ (gitignored). + * + * Ref defaults to package.json config.localnet_splice_version (0.6.14). + * Pattern mirrors Fairmint/daml scripts/sync-splice-dars.ts. + */ + +import { execFileSync } from 'node:child_process'; +import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join, relative } from 'node:path'; + +import { + computeSha256, + SPLICE_TEST_TOKEN_V2_DAR_FILENAME, + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH, + spliceTestTokenV2DarPath, +} from '../src/testing/cip56/fixture'; + +const ROOT_DIR = process.cwd(); +const SPLICE_REPO = process.env['SPLICE_REPO'] ?? 'https://github.com/canton-network/splice.git'; + +function readSpliceRef(): string { + if (process.env['SPLICE_REF']) { + return process.env['SPLICE_REF']; + } + + const pkg = JSON.parse(readFileSync(join(ROOT_DIR, 'package.json'), 'utf8')) as { + config?: { localnet_splice_version?: string }; + }; + const version = pkg.config?.localnet_splice_version; + if (!version) { + throw new Error('package.json config.localnet_splice_version is required'); + } + return version; +} + +function parseArgs(): { force: boolean } { + return { force: process.argv.includes('--force') }; +} + +function runGit(args: string[], cwd?: string): void { + execFileSync('git', args, { + cwd, + stdio: 'inherit', + }); +} + +function main(): void { + const { force } = parseArgs(); + const spliceRef = readSpliceRef(); + const targetPath = spliceTestTokenV2DarPath(ROOT_DIR); + + if (!force && existsSync(targetPath)) { + const actual = computeSha256(targetPath); + if (actual === SPLICE_TEST_TOKEN_V2_SHA256) { + console.log( + `Splice TestTokenV2 DAR already present and verified at ${relative(ROOT_DIR, targetPath)}` + ); + console.log(` package id: ${SPLICE_TEST_TOKEN_V2_PACKAGE_ID}`); + return; + } + throw new Error( + `Hash mismatch for existing ${relative(ROOT_DIR, targetPath)}: expected ${SPLICE_TEST_TOKEN_V2_SHA256}, got ${actual}. Re-run with --force.` + ); + } + + const tempDir = mkdtempSync(join(tmpdir(), 'canton-dev-tools-splice-token-v2-')); + const cloneDir = join(tempDir, 'splice'); + + try { + console.log( + `Fetching ${SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH} from ${SPLICE_REPO} at ${spliceRef}` + ); + runGit(['clone', '--filter=blob:none', '--sparse', '--no-checkout', SPLICE_REPO, cloneDir]); + runGit(['checkout', spliceRef], cloneDir); + runGit(['sparse-checkout', 'set', 'daml/dars'], cloneDir); + + const sourcePath = join(cloneDir, SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH); + if (!existsSync(sourcePath)) { + throw new Error(`Missing upstream Splice DAR: ${SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH}`); + } + + const sourceSha = computeSha256(sourcePath); + if (sourceSha !== SPLICE_TEST_TOKEN_V2_SHA256) { + throw new Error( + `Hash mismatch for upstream ${SPLICE_TEST_TOKEN_V2_DAR_FILENAME}: expected ${SPLICE_TEST_TOKEN_V2_SHA256}, got ${sourceSha}` + ); + } + + mkdirSync(dirname(targetPath), { recursive: true }); + copyFileSync(sourcePath, targetPath); + + const copiedSha = computeSha256(targetPath); + if (copiedSha !== SPLICE_TEST_TOKEN_V2_SHA256) { + throw new Error(`Hash mismatch after copying ${SPLICE_TEST_TOKEN_V2_DAR_FILENAME}`); + } + + console.log(`Wrote ${relative(ROOT_DIR, targetPath)}`); + console.log(` sha256: ${copiedSha}`); + console.log(` package id (assert after upload): ${SPLICE_TEST_TOKEN_V2_PACKAGE_ID}`); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } +} + +try { + main(); +} catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +} diff --git a/scripts/localnet-cloud.sh b/scripts/localnet-cloud.sh new file mode 100755 index 0000000..6b83577 --- /dev/null +++ b/scripts/localnet-cloud.sh @@ -0,0 +1,1176 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +PROJECT_ROOT="${CANTON_LOCALNET_PROJECT_ROOT:-$(pwd)}" +QUICKSTART_DIR="${CANTON_LOCALNET_QUICKSTART_DIR:-${REPO_ROOT}/libs/cn-quickstart/quickstart}" +DOCKERD_PID_FILE="/tmp/localnet-dockerd.pid" +DOCKERD_LOG_FILE="/tmp/localnet-dockerd.log" +HOSTS_ENTRY="127.0.0.1 scan.localhost sv.localhost wallet.localhost" +CURL_CONNECT_TIMEOUT=2 +CURL_MAX_TIME=5 +LOCALNET_SPLICE_VERSION="${CANTON_LOCALNET_SPLICE_VERSION:-}" +LOCALNET_SCRIBE_VERSION="${CANTON_LOCALNET_SCRIBE_VERSION:-}" +LOCALNET_PROTOCOL_VERSION="${CANTON_LOCALNET_PROTOCOL_VERSION:-}" +VALIDATOR_READY_ATTEMPTS="${CANTON_LOCALNET_VALIDATOR_READY_ATTEMPTS:-90}" +SCAN_READY_ATTEMPTS="${CANTON_LOCALNET_SCAN_READY_ATTEMPTS:-90}" +SPLICE_CONFIG_PENDING_KEY="CANTON_NODE_SDK_SPLICE_CONFIG_PENDING" +SPLICE_CONFIG_CHANGED="false" + +log() { + printf '[localnet] %s\n' "$*" +} + +is_truthy() { + # Portable lowercase (Bash 3.2+ / macOS /bin/bash); avoid ${1,,} (Bash 4+). + case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in + 1 | true | yes | on) + return 0 + ;; + *) + return 1 + ;; + esac +} + +require_command() { + local cmd="$1" + if ! command -v "${cmd}" >/dev/null 2>&1; then + log "Missing required command: ${cmd}" + exit 1 + fi +} + +require_positive_integer() { + local name="$1" + local value="$2" + + if [[ ! "${value}" =~ ^[1-9][0-9]*$ ]]; then + log "${name} must be a positive integer; received '${value}'." + exit 1 + fi +} + +validate_configuration() { + require_positive_integer "CANTON_LOCALNET_VALIDATOR_READY_ATTEMPTS" "${VALIDATOR_READY_ATTEMPTS}" + require_positive_integer "CANTON_LOCALNET_SCAN_READY_ATTEMPTS" "${SCAN_READY_ATTEMPTS}" +} + +ensure_sudo() { + if ! sudo_noninteractive_available; then + log "Passwordless sudo is required in this cloud environment." + exit 1 + fi +} + +sudo_noninteractive_available() { + command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1 +} + +ensure_docker_packages() { + if command -v docker >/dev/null 2>&1 \ + && docker compose version >/dev/null 2>&1; then + return + fi + + if ! sudo_noninteractive_available || ! command -v apt-get >/dev/null 2>&1; then + log "Docker with Compose is required. Install Docker and start it, then retry." + exit 1 + fi + + log "Installing Docker packages..." + sudo apt-get update + sudo apt-get install -y docker.io docker-compose docker-compose-v2 +} + +ensure_legacy_iptables() { + if ! command -v update-alternatives >/dev/null 2>&1 \ + || [[ ! -x /usr/sbin/iptables-legacy || ! -x /usr/sbin/ip6tables-legacy ]]; then + log "iptables legacy binaries unavailable; skipping backend switch." + return + fi + + if ! iptables --version 2>/dev/null | grep -q 'legacy'; then + log "Switching iptables to legacy backend..." + sudo update-alternatives --set iptables /usr/sbin/iptables-legacy + fi + + if ! ip6tables --version 2>/dev/null | grep -q 'legacy'; then + log "Switching ip6tables to legacy backend..." + sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy + fi +} + +docker_ready() { + docker info >/dev/null 2>&1 || (sudo_noninteractive_available && sudo docker info >/dev/null 2>&1) +} + +configure_docker_socket_permissions() { + if [[ ! -S /var/run/docker.sock ]]; then + return + fi + + if ! sudo_noninteractive_available; then + return + fi + + sudo groupadd -f docker >/dev/null 2>&1 || true + sudo chown root:docker /var/run/docker.sock >/dev/null 2>&1 || true + sudo chmod 660 /var/run/docker.sock || true +} + +run_docker() { + if docker info >/dev/null 2>&1; then + docker "$@" + return + fi + if ! sudo_noninteractive_available || ! sudo docker info >/dev/null 2>&1; then + log "Docker daemon is not available." + exit 1 + fi + sudo docker "$@" +} + +resolve_quickstart_image_tag() { + local env_file="" + local splice_version="" + local parsed_value="" + + for env_file in "${QUICKSTART_DIR}/.env" "${QUICKSTART_DIR}/.env.local"; do + if [[ ! -f "${env_file}" ]]; then + continue + fi + + parsed_value="$(awk -F= -v key="SPLICE_VERSION" ' + $0 ~ /^[[:space:]]*#/ { next } + $1 == key { + value = substr($0, index($0, "=") + 1) + gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) + gsub(/^"|"$/, "", value) + gsub(/^'\''|'\''$/, "", value) + parsed = value + } + END { + if (parsed != "") { + print parsed + } + } + ' "${env_file}")" + + if [[ -n "${parsed_value}" ]]; then + splice_version="${parsed_value}" + fi + done + + printf '%s' "${splice_version}" +} + +run_quickstart_command() { + local command="$1" + local docker_shim_dir="" + local quickstart_image_tag="" + local quickstart_path="${HOME}/.dpm/bin:${HOME}/.daml/bin:${PATH}" + local status=0 + + quickstart_image_tag="$(resolve_quickstart_image_tag)" + + if ! docker info >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then + docker_shim_dir="$(mktemp -d "/tmp/canton-localnet-docker-shim.XXXXXX")" + cat >"${docker_shim_dir}/docker" <<'EOF' +#!/usr/bin/env bash +exec sudo -E docker "$@" +EOF + chmod +x "${docker_shim_dir}/docker" + log "Using sudo docker shim for quickstart command." + fi + + if [[ -n "${docker_shim_dir}" ]]; then + quickstart_path="${docker_shim_dir}:${quickstart_path}" + fi + + set +e + ( + cd "${QUICKSTART_DIR}" + MODULES_DIR="${QUICKSTART_DIR}/docker/modules" \ + LOCALNET_DIR="${QUICKSTART_DIR}/docker/modules/localnet" \ + IMAGE_TAG="${quickstart_image_tag}" \ + PATH="${quickstart_path}" \ + bash -lc "${command}" + ) + status=$? + set -e + + if [[ -n "${docker_shim_dir}" ]]; then + rm -rf "${docker_shim_dir}" + fi + + return "${status}" +} + +run_quickstart_make() { + local target="$1" + run_quickstart_command "make ${target}" +} + +run_infra_compose() { + local compose_args="$1" + local keycloak_compose_file="" + local keycloak_env_file="" + local keycloak_profile="" + + if [[ "$(current_auth_mode)" == "oauth2" ]]; then + keycloak_compose_file='-f "${MODULES_DIR}/keycloak/compose.yaml"' + keycloak_env_file='--env-file "${MODULES_DIR}/keycloak/compose.env"' + keycloak_profile='--profile keycloak' + fi + + run_quickstart_command "docker compose \ + -f \"\${LOCALNET_DIR}/compose.yaml\" \ + ${keycloak_compose_file} \ + --env-file .env \ + --env-file .env.local \ + --env-file \"\${LOCALNET_DIR}/compose.env\" \ + --env-file \"\${LOCALNET_DIR}/env/common.env\" \ + ${keycloak_env_file} \ + --profile app-provider \ + --profile app-user \ + --profile sv \ + ${keycloak_profile} \ + ${compose_args}" +} + +start_docker_daemon() { + local dockerd_pid="" + + if docker_ready; then + configure_docker_socket_permissions + return + fi + + if ! sudo_noninteractive_available; then + log "Docker daemon is not running. Start Docker and retry." + exit 1 + fi + + ensure_legacy_iptables + + log "Starting Docker daemon with vfs storage driver..." + sudo nohup dockerd --host=unix:///var/run/docker.sock --pidfile="${DOCKERD_PID_FILE}" --storage-driver=vfs >"${DOCKERD_LOG_FILE}" 2>&1 & + + for _ in $(seq 1 60); do + if sudo docker info >/dev/null 2>&1; then + configure_docker_socket_permissions + return + fi + if [[ -f "${DOCKERD_PID_FILE}" ]]; then + dockerd_pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)" + if [[ -n "${dockerd_pid}" ]] && ! ps -p "${dockerd_pid}" >/dev/null 2>&1; then + log "Docker daemon exited before becoming ready." + log "Inspect ${DOCKERD_LOG_FILE}." + exit 1 + fi + fi + sleep 1 + done + + log "Docker failed to start. Inspect ${DOCKERD_LOG_FILE}." + exit 1 +} + +ensure_submodules() { + if [[ -n "${CANTON_LOCALNET_QUICKSTART_DIR:-}" ]]; then + if [[ -d "${QUICKSTART_DIR}" ]]; then + return + fi + log "Configured CANTON_LOCALNET_QUICKSTART_DIR does not exist: ${QUICKSTART_DIR}" + exit 1 + fi + + if [[ -d "${REPO_ROOT}/libs/splice" && -d "${QUICKSTART_DIR}" ]]; then + return + fi + + require_command git + + if ! git -C "${REPO_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + log "Missing localnet assets. Set CANTON_LOCALNET_QUICKSTART_DIR or use the canton-dev-tools CLI to fetch cn-quickstart into the cache." + exit 1 + fi + + if [[ ! -d "${REPO_ROOT}/libs/splice" ]]; then + log "Initializing libs/splice submodule..." + git -C "${REPO_ROOT}" submodule update --init --depth 1 libs/splice + fi + + if [[ ! -d "${REPO_ROOT}/libs/cn-quickstart" ]]; then + log "Initializing libs/cn-quickstart submodule..." + git -C "${REPO_ROOT}" submodule update --init --recursive libs/cn-quickstart + fi + + if [[ ! -d "${QUICKSTART_DIR}" ]]; then + log "cn-quickstart directory not found after submodule init." + exit 1 + fi +} + +ensure_hosts_entries() { + if ! grep -Eq '(^|[[:space:]])scan\.localhost([[:space:]]|$)' /etc/hosts \ + || ! grep -Eq '(^|[[:space:]])sv\.localhost([[:space:]]|$)' /etc/hosts \ + || ! grep -Eq '(^|[[:space:]])wallet\.localhost([[:space:]]|$)' /etc/hosts; then + log "Adding localnet host aliases to /etc/hosts..." + if sudo_noninteractive_available; then + echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null + return + fi + if [[ -t 0 ]] && command -v sudo >/dev/null 2>&1; then + echo "${HOSTS_ENTRY}" | sudo tee -a /etc/hosts >/dev/null + return + fi + log "Missing localnet host aliases. Add this line to /etc/hosts, then retry:" + log "${HOSTS_ENTRY}" + exit 1 + fi +} + +set_quickstart_env_value() { + local file="$1" + local key="$2" + local value="$3" + + if [[ ! -f "${file}" ]]; then + return + fi + + if grep -Eq "^${key}=" "${file}"; then + KEY="${key}" VALUE="${value}" perl -0pi -e ' + my $key = $ENV{"KEY"}; + my $value = $ENV{"VALUE"}; + s/^\Q$key\E=.*/$key=$value/m; + ' "${file}" + else + printf '\n%s=%s\n' "${key}" "${value}" >>"${file}" + fi +} + +patch_quickstart_canton_healthcheck() { + local healthcheck="${QUICKSTART_DIR}/docker/modules/localnet/docker/canton/health-check.sh" + + if [[ ! -f "${healthcheck}" ]]; then + log "Canton quickstart healthcheck not found: ${healthcheck}" + exit 1 + fi + + cat >"${healthcheck}" <<'EOF' +#!/bin/bash +set -euo pipefail + +http_check() { + local port="$1" + local path="$2" + local status="" + + echo "Checking http://localhost:${port}${path}" + exec 3<>"/dev/tcp/127.0.0.1/${port}" + printf 'GET %s HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' "${path}" >&3 + IFS=$'\r' read -r status <&3 || true + exec 3<&- + exec 3>&- + + case "${status}" in + HTTP/*\ 2*) ;; + *) + echo "Unexpected status from ${port}${path}: ${status}" + return 1 + ;; + esac +} + +if [ "${APP_USER_PROFILE:-off}" = "on" ]; then + http_check "2${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health" +fi +if [ "${APP_PROVIDER_PROFILE:-off}" = "on" ]; then + http_check "3${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health" +fi +if [ "${SV_PROFILE:-off}" = "on" ]; then + http_check "4${CANTON_HTTP_HEALTHCHECK_PORT_SUFFIX}" "/health" +fi +EOF + chmod +x "${healthcheck}" +} + +patch_quickstart_splice_healthcheck() { + local healthcheck="${QUICKSTART_DIR}/docker/modules/localnet/docker/splice/health-check.sh" + + if [[ ! -f "${healthcheck}" ]]; then + log "Splice quickstart healthcheck not found: ${healthcheck}" + exit 1 + fi + + cat >"${healthcheck}" <<'EOF' +#!/bin/bash +set -euo pipefail + +http_check() { + local port="$1" + local path="$2" + local status="" + + echo "Checking http://localhost:${port}${path}" + exec 3<>"/dev/tcp/127.0.0.1/${port}" + printf 'GET %s HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' "${path}" >&3 + IFS=$'\r' read -r status <&3 || true + exec 3<&- + exec 3>&- + + case "${status}" in + HTTP/*\ 2*) ;; + *) + echo "Unexpected status from ${port}${path}: ${status}" + return 1 + ;; + esac +} + +if [ "${APP_USER_PROFILE:-off}" = "on" ]; then + http_check "2${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz" +fi +if [ "${APP_PROVIDER_PROFILE:-off}" = "on" ]; then + http_check "3${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz" +fi +if [ "${SV_PROFILE:-off}" = "on" ]; then + http_check "4${VALIDATOR_ADMIN_API_PORT_SUFFIX}" "/api/validator/readyz" + http_check "5012" "/api/scan/readyz" + http_check "5014" "/api/sv/readyz" +fi +EOF + chmod +x "${healthcheck}" +} + +configure_quickstart_localnet() { + local env_file="" + local canton_conf="${QUICKSTART_DIR}/docker/modules/localnet/conf/canton/app.conf" + local splice_sv_conf="${QUICKSTART_DIR}/docker/modules/localnet/conf/splice/sv/app.conf" + + SPLICE_CONFIG_CHANGED="false" + if [[ "$(quickstart_env_value "${SPLICE_CONFIG_PENDING_KEY}")" == "true" ]]; then + SPLICE_CONFIG_CHANGED="true" + fi + + for env_file in "${QUICKSTART_DIR}/.env" "${QUICKSTART_DIR}/.env.local"; do + if [[ -n "${LOCALNET_SPLICE_VERSION}" ]]; then + set_quickstart_env_value "${env_file}" "SPLICE_VERSION" "${LOCALNET_SPLICE_VERSION}" + fi + if [[ -n "${LOCALNET_SCRIBE_VERSION}" ]]; then + set_quickstart_env_value "${env_file}" "SCRIBE_VERSION" "${LOCALNET_SCRIBE_VERSION}" + fi + if [[ -n "${CANTON_LOCALNET_DAML_RUNTIME_VERSION:-}" ]]; then + set_quickstart_env_value "${env_file}" "DAML_RUNTIME_VERSION" "${CANTON_LOCALNET_DAML_RUNTIME_VERSION}" + fi + done + + if [[ ! -f "${canton_conf}" ]]; then + log "Canton quickstart config not found: ${canton_conf}" + exit 1 + fi + + if [[ ! -f "${splice_sv_conf}" ]]; then + log "Splice SV quickstart config not found: ${splice_sv_conf}" + exit 1 + fi + + if ! grep -Eq '^[[:space:]]*canton\.scan-apps\.scan-app\.enable-forced-acs-snapshots[[:space:]]*=[[:space:]]*(true|yes|on)[[:space:]]*$' "${splice_sv_conf}"; then + cat >>"${splice_sv_conf}" <<'EOF' + +# LocalNet integration tests use this endpoint to create deterministic snapshot-backed fixtures. +canton.scan-apps.scan-app.enable-forced-acs-snapshots = true +EOF + set_quickstart_env_value "${QUICKSTART_DIR}/.env.local" "${SPLICE_CONFIG_PENDING_KEY}" "true" + SPLICE_CONFIG_CHANGED="true" + fi + + if [[ -n "${LOCALNET_PROTOCOL_VERSION}" ]]; then + PROTOCOL_VERSION="${LOCALNET_PROTOCOL_VERSION}" perl -0pi -e ' + my $protocol_version = $ENV{"PROTOCOL_VERSION"}; + s/initial-protocol-version = \d+/initial-protocol-version = $protocol_version/g; + s/(non-standard-config = yes\n)(?![[:space:]]*alpha-version-support = yes)/$1 alpha-version-support = yes\n/; + s/(initial-protocol-version = \d+\n)(?![[:space:]]*alpha-version-support = yes)/$1 alpha-version-support = yes\n/; + ' "${canton_conf}" + fi + + patch_quickstart_canton_healthcheck + patch_quickstart_splice_healthcheck +} + +requested_auth_mode() { + printf '%s' "${CANTON_LOCALNET_AUTH_MODE:-oauth2}" +} + +quickstart_env_value() { + local key="$1" + local parsed_value="" + + if [[ -f "${QUICKSTART_DIR}/.env.local" ]]; then + parsed_value="$(awk -F= -v key="${key}" ' + $0 ~ /^[[:space:]]*#/ { next } + $1 == key { + value = substr($0, index($0, "=") + 1) + gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) + gsub(/^"|"$/, "", value) + gsub(/^'\''|'\''$/, "", value) + parsed = value + } + END { + if (parsed != "") { + print parsed + } + } + ' "${QUICKSTART_DIR}/.env.local")" + fi + + printf '%s' "${parsed_value}" +} + +configured_auth_mode() { + quickstart_env_value "AUTH_MODE" +} + +quickstart_profile_config_complete() { + local key="" + + for key in OBSERVABILITY_ENABLED AUTH_MODE PARTY_HINT TEST_MODE; do + if [[ -z "$(quickstart_env_value "${key}")" ]]; then + return 1 + fi + done + + return 0 +} + +verify_configured_auth_mode() { + local expected_auth_mode="$1" + local actual_auth_mode="" + + actual_auth_mode="$(configured_auth_mode)" + if [[ "${actual_auth_mode}" != "${expected_auth_mode}" ]]; then + log "cn-quickstart setup did not configure AUTH_MODE=${expected_auth_mode}; found '${actual_auth_mode:-unset}'." + exit 1 + fi +} + +current_auth_mode() { + local configured="" + + configured="$(configured_auth_mode)" + if [[ -n "${configured}" ]]; then + printf '%s' "${configured}" + return + fi + + requested_auth_mode +} + +run_quickstart_setup() { + local auth_mode="" + + auth_mode="$(requested_auth_mode)" + case "${auth_mode}" in + shared-secret) + log "Running cn-quickstart setup (shared-secret mode)..." + ( + cd "${QUICKSTART_DIR}" + printf 'Y\nn\n\n' | make setup || true + ) + verify_configured_auth_mode "shared-secret" + ;; + oauth2) + log "Running cn-quickstart setup (OAuth2 enabled)..." + ( + cd "${QUICKSTART_DIR}" + # Match CI behavior first, then fall back to prompt-based answers for newer setup flows. + echo "2" | make setup || true + if [[ "$(configured_auth_mode)" != "oauth2" ]]; then + printf 'y\ny\n\nn\n' | make setup + fi + ) + verify_configured_auth_mode "oauth2" + ;; + *) + log "Unsupported CANTON_LOCALNET_AUTH_MODE: ${auth_mode}" + exit 1 + ;; + esac +} + +quickstart_setup() { + if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then + run_quickstart_setup + elif ! quickstart_profile_config_complete; then + log "Regenerating incomplete cn-quickstart config." + rm -f "${QUICKSTART_DIR}/.env.local" + run_quickstart_setup + elif [[ -n "${CANTON_LOCALNET_AUTH_MODE:-}" && "$(configured_auth_mode)" != "${CANTON_LOCALNET_AUTH_MODE}" ]]; then + log "Regenerating cn-quickstart config for CANTON_LOCALNET_AUTH_MODE=${CANTON_LOCALNET_AUTH_MODE}." + rm -f "${QUICKSTART_DIR}/.env.local" + run_quickstart_setup + else + log "Reusing existing ${QUICKSTART_DIR}/.env.local." + fi + + if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then + log "cn-quickstart setup failed: ${QUICKSTART_DIR}/.env.local was not created." + exit 1 + fi + + configure_quickstart_localnet + + if quickstart_infra_only_enabled; then + log "Skipping Daml SDK install for infrastructure-only localnet." + return + fi + + if [[ ! -x "${HOME}/.daml/bin/daml" ]]; then + log "Installing Daml SDK..." + ( + cd "${QUICKSTART_DIR}" + make install-daml-sdk + ) + else + log "Reusing existing Daml SDK at ${HOME}/.daml/bin/daml." + fi +} + +quickstart_fast_start_enabled() { + is_truthy "${CANTON_LOCALNET_FAST_START:-true}" +} + +quickstart_force_full_start() { + is_truthy "${CANTON_LOCALNET_FORCE_FULL_START:-false}" +} + +quickstart_infra_only_enabled() { + is_truthy "${CANTON_LOCALNET_INFRA_ONLY:-false}" +} + +quickstart_build_artifacts_ready() { + local missing_paths=() + + if [[ ! -f "${QUICKSTART_DIR}/backend/build/distributions/backend.tar" ]]; then + missing_paths+=("backend/build/distributions/backend.tar") + fi + + if [[ ! -d "${QUICKSTART_DIR}/frontend/dist" ]]; then + missing_paths+=("frontend/dist") + fi + + if ! compgen -G "${QUICKSTART_DIR}/daml/licensing/.daml/dist/*.dar" >/dev/null; then + missing_paths+=("daml/licensing/.daml/dist/*.dar") + fi + + if ! compgen -G "${QUICKSTART_DIR}/backend/build/otel-agent/opentelemetry-javaagent-*.jar" >/dev/null; then + missing_paths+=("backend/build/otel-agent/opentelemetry-javaagent-*.jar") + fi + + if [[ ${#missing_paths[@]} -gt 0 ]]; then + log "Fast start unavailable; missing quickstart build artifacts: ${missing_paths[*]}" + return 1 + fi + + return 0 +} + +extract_quickstart_compose_up_command() { + ( + cd "${QUICKSTART_DIR}" + make -n start 2>/dev/null | awk '/docker compose .* up -d --no-recreate/{line=$0} END{print line}' + ) +} + +try_fast_start_localnet() { + local compose_up_command="" + + compose_up_command="$(extract_quickstart_compose_up_command)" + if [[ -z "${compose_up_command}" ]]; then + log "Unable to determine quickstart compose startup command." + return 1 + fi + + log "Starting cn-quickstart with fast path (skip quickstart rebuild)..." + run_quickstart_command "${compose_up_command}" +} + +start_infra_only_localnet() { + log "Starting cn-quickstart LocalNet infrastructure only (skip quickstart app, PQS, and onboarding)." + run_infra_compose 'up -d --no-recreate' +} + +splice_container_running() { + local running="" + + running="$(run_docker inspect --format '{{.State.Running}}' splice 2>/dev/null || true)" + [[ "${running}" == "true" ]] +} + +recreate_splice_after_config_change() { + local should_recreate="$1" + + if [[ "${should_recreate}" != "true" ]]; then + return + fi + + log "Recreating the running Splice service to apply updated LocalNet configuration." + run_infra_compose 'up -d --no-deps --force-recreate splice' +} + +mark_splice_config_applied() { + if [[ "${SPLICE_CONFIG_CHANGED}" != "true" ]]; then + return + fi + + set_quickstart_env_value "${QUICKSTART_DIR}/.env.local" "${SPLICE_CONFIG_PENDING_KEY}" "false" + SPLICE_CONFIG_CHANGED="false" +} + +stop_infra_only_localnet() { + if [[ ! -f "${QUICKSTART_DIR}/.env.local" ]]; then + return + fi + + log "Stopping cn-quickstart LocalNet infrastructure-only stack..." + run_infra_compose down || true +} + +wait_for_services() { + local auth_mode="" + local code="" + local ledger_code="" + + auth_mode="$(current_auth_mode)" + + if [[ "${auth_mode}" == "oauth2" ]]; then + log "Waiting for Keycloak..." + for _ in $(seq 1 30); do + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then + break + fi + sleep 2 + done + if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then + log "Keycloak did not become ready." + exit 1 + fi + else + log "Checking for Keycloak (optional in ${auth_mode} mode)..." + for _ in $(seq 1 5); do + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then + log "Keycloak is ready." + break + fi + sleep 2 + done + fi + + log "Waiting for Validator API..." + for _ in $(seq 1 "${VALIDATOR_READY_ATTEMPTS}"); do + code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)" + if [[ "${code}" == "200" || "${code}" == "401" ]]; then + break + fi + sleep 2 + done + code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)" + if [[ "${code}" != "200" && "${code}" != "401" ]]; then + log "Validator API did not become ready." + exit 1 + fi + + log "Waiting for Scan API..." + for _ in $(seq 1 "${SCAN_READY_ATTEMPTS}"); do + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then + break + fi + sleep 2 + done + if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then + log "Scan API did not become ready." + exit 1 + fi + + log "Waiting for Ledger JSON API..." + for _ in $(seq 1 60); do + ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)" + if [[ "${ledger_code}" == "200" || "${ledger_code}" == "401" ]]; then + break + fi + sleep 2 + done + ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)" + if [[ "${ledger_code}" != "200" && "${ledger_code}" != "401" ]]; then + log "Ledger JSON API did not become ready (HTTP ${ledger_code})." + exit 1 + fi + + log "All localnet services are ready." +} + +start_localnet() { + local should_recreate_splice="false" + + if [[ "${SPLICE_CONFIG_CHANGED}" == "true" ]] && splice_container_running; then + should_recreate_splice="true" + fi + + if quickstart_force_full_start; then + log "Forcing full cn-quickstart start (CANTON_LOCALNET_FORCE_FULL_START=true)." + run_quickstart_make start + recreate_splice_after_config_change "${should_recreate_splice}" + wait_for_services + mark_splice_config_applied + return + fi + + if quickstart_infra_only_enabled; then + start_infra_only_localnet + recreate_splice_after_config_change "${should_recreate_splice}" + wait_for_services + mark_splice_config_applied + return + fi + + if quickstart_fast_start_enabled && quickstart_build_artifacts_ready; then + if try_fast_start_localnet; then + recreate_splice_after_config_change "${should_recreate_splice}" + wait_for_services + mark_splice_config_applied + return + fi + log "Fast start failed; falling back to full cn-quickstart start." + fi + + log "Starting cn-quickstart with full build..." + run_quickstart_make start + recreate_splice_after_config_change "${should_recreate_splice}" + wait_for_services + mark_splice_config_applied +} + +stop_localnet() { + if [[ ! -d "${QUICKSTART_DIR}" ]]; then + log "cn-quickstart directory not found; nothing to stop." + stop_managed_dockerd + return + fi + + log "Stopping cn-quickstart..." + stop_infra_only_localnet + if [[ -f "${QUICKSTART_DIR}/Makefile" ]]; then + run_quickstart_make stop || true + else + log "Quickstart Makefile not found; skipping quickstart stop target." + fi + stop_managed_dockerd +} + +stop_managed_dockerd() { + local pid="" + local cmd="" + + if [[ ! -f "${DOCKERD_PID_FILE}" ]]; then + return + fi + + pid="$(cat "${DOCKERD_PID_FILE}" 2>/dev/null || true)" + if [[ -z "${pid}" ]]; then + rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}" + return + fi + + if ! ps -p "${pid}" >/dev/null 2>&1; then + rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}" + return + fi + + cmd="$(ps -p "${pid}" -o comm= 2>/dev/null | tr -d '[:space:]')" + if [[ "${cmd}" != "dockerd" ]]; then + log "PID ${pid} is not dockerd; skipping daemon cleanup." + rm -f "${DOCKERD_PID_FILE}" + return + fi + + if ! sudo -n true >/dev/null 2>&1; then + log "Cannot stop managed dockerd without passwordless sudo." + return + fi + + log "Stopping managed dockerd (pid ${pid})..." + sudo kill -TERM "${pid}" >/dev/null 2>&1 || true + for _ in $(seq 1 10); do + if ! ps -p "${pid}" >/dev/null 2>&1; then + break + fi + sleep 1 + done + if ps -p "${pid}" >/dev/null 2>&1; then + sudo kill -KILL "${pid}" >/dev/null 2>&1 || true + fi + rm -f "${DOCKERD_PID_FILE}" "${DOCKERD_LOG_FILE}" +} + +status_localnet() { + if docker_ready; then + log "Docker daemon is running." + else + log "Docker daemon is not running." + exit 1 + fi + + run_docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' + echo + + keycloak_ok="no" + validator_ok="no" + scan_ok="no" + ledger_ok="no" + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then + keycloak_ok="yes" + fi + validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)" + if [[ "${validator_code}" == "200" || "${validator_code}" == "401" ]]; then + validator_ok="yes" + fi + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then + scan_ok="yes" + fi + ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)" + if [[ "${ledger_code}" == "200" || "${ledger_code}" == "401" ]]; then + ledger_ok="yes" + fi + + printf 'Keycloak ready: %s\n' "${keycloak_ok}" + printf 'Validator ready: %s (HTTP %s)\n' "${validator_ok}" "${validator_code:-n/a}" + printf 'Scan ready: %s\n' "${scan_ok}" + printf 'Ledger JSON API ready: %s (HTTP %s)\n' "${ledger_ok}" "${ledger_code:-n/a}" +} + +show_localnet_logs() { + log "==================== Docker Containers ====================" + if docker_ready; then + run_docker ps -a || true + else + log "Docker daemon is not running." + fi + + echo + log "==================== Docker Compose Logs ====================" + if [[ -f "${QUICKSTART_DIR}/.env.local" ]]; then + run_infra_compose 'logs --tail=100' || true + elif [[ -d "${QUICKSTART_DIR}" ]]; then + log "Quickstart local env not found; skipping compose logs: ${QUICKSTART_DIR}/.env.local" + else + log "cn-quickstart directory not found: ${QUICKSTART_DIR}" + fi + + echo + log "==================== Canton Logs ====================" + if [[ -f "${QUICKSTART_DIR}/logs/canton.log" ]]; then + tail -100 "${QUICKSTART_DIR}/logs/canton.log" || true + else + log "Canton log not found: ${QUICKSTART_DIR}/logs/canton.log" + fi + + if [[ -f "${DOCKERD_LOG_FILE}" ]]; then + echo + log "==================== Managed Docker Daemon Logs ====================" + tail -100 "${DOCKERD_LOG_FILE}" || true + fi +} + +run_smoke() { + local auth_mode="" + local validator_code="" + local ledger_code="" + + log "Running localnet smoke checks..." + auth_mode="$(current_auth_mode)" + + if curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://localhost:8082/realms/AppProvider >/dev/null 2>&1; then + log "Keycloak is reachable." + elif [[ "${auth_mode}" == "oauth2" ]]; then + log "Keycloak is not reachable." + exit 1 + else + log "Keycloak not detected (expected in ${auth_mode} mode)." + fi + + validator_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3903/api/validator/v0/wallet/user-status || true)" + if [[ "${validator_code}" != "200" && "${validator_code}" != "401" ]]; then + log "Validator API is not reachable (HTTP ${validator_code})." + exit 1 + fi + + if ! curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -fsS http://scan.localhost:4000/api/scan/v0/dso-party-id >/dev/null 2>&1; then + log "Scan API is not reachable." + exit 1 + fi + + ledger_code="$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time "${CURL_MAX_TIME}" -sS -o /dev/null -w '%{http_code}' http://localhost:3975/v2/version || true)" + if [[ "${ledger_code}" != "200" && "${ledger_code}" != "401" ]]; then + log "Ledger JSON API is not reachable (HTTP ${ledger_code})." + exit 1 + fi + + log "Smoke checks passed." +} + +read_npm_script() { + local target_dir="$1" + local script_name="$2" + + if [[ ! -f "${target_dir}/package.json" ]]; then + return + fi + + node -e 'const fs=require("fs"); const pkgPath=process.argv[1]; const scriptName=process.argv[2]; try { const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); const script = pkg?.scripts?.[scriptName]; if (typeof script === "string") process.stdout.write(script); } catch {}' "${target_dir}/package.json" "${script_name}" +} + +script_is_recursive_localnet_test() { + local script_value="$1" + # Match bare binaries, scoped package npx, and direct script invocations. + echo "${script_value}" | grep -Eq '(^|[[:space:]])((npx[[:space:]]+)?@fairmint/canton-dev-tools[[:space:]]+test|canton-(localnet|dev-tools)[[:space:]]+test|[^[:space:]]*localnet-cloud\.sh[[:space:]]+test)([[:space:]]|$)' +} + +run_integration_tests() { + local script_value="" + + if [[ -n "${CANTON_LOCALNET_TEST_CMD:-}" ]]; then + log "Running custom integration command from CANTON_LOCALNET_TEST_CMD..." + ( + cd "${PROJECT_ROOT}" + bash -lc "${CANTON_LOCALNET_TEST_CMD}" + ) + return + fi + + script_value="$(read_npm_script "${PROJECT_ROOT}" "test:integration")" + if [[ -n "${script_value}" ]]; then + if script_is_recursive_localnet_test "${script_value}"; then + log "Skipping recursive test:integration script." + else + log "Running project integration tests: npm run test:integration" + ( + cd "${PROJECT_ROOT}" + npm run test:integration + ) + return + fi + fi + + script_value="$(read_npm_script "${PROJECT_ROOT}" "test:localnet")" + if [[ -n "${script_value}" ]]; then + if script_is_recursive_localnet_test "${script_value}"; then + log "Skipping recursive test:localnet script." + else + log "Running project integration tests: npm run test:localnet" + ( + cd "${PROJECT_ROOT}" + npm run test:localnet + ) + return + fi + fi + + log "No integration test command configured; skipping test step." +} + +usage() { + cat <<'USAGE' +Usage: scripts/localnet-cloud.sh + +Product commands: + start Start localnet and wait for ready endpoints + readiness Run endpoint readiness / smoke checks + diagnostics Show localnet diagnostic logs + teardown Stop localnet services + +Legacy aliases: + setup Install prerequisites, configure quickstart + stop Alias for teardown + logs Alias for diagnostics + status Show docker + endpoint status + smoke Alias for readiness + test Run project integration tests (if configured) + verify Run setup + start + readiness + test + +Environment: + CANTON_LOCALNET_FAST_START=true|false Enable fast startup path (default: true) + CANTON_LOCALNET_FORCE_FULL_START=true|false Force full startup with rebuild + CANTON_LOCALNET_INFRA_ONLY=true|false Start only LocalNet + Keycloak infrastructure + CANTON_LOCALNET_AUTH_MODE=oauth2|shared-secret + CANTON_LOCALNET_QUICKSTART_DIR= Use an existing cn-quickstart/quickstart directory +USAGE +} + +main() { + if [[ "${1:-}" == "" ]]; then + usage + exit 1 + fi + + case "$1" in + setup) + validate_configuration + ensure_docker_packages + start_docker_daemon + ensure_submodules + ensure_hosts_entries + quickstart_setup + ;; + start) + validate_configuration + require_command curl + ensure_docker_packages + start_docker_daemon + ensure_submodules + ensure_hosts_entries + quickstart_setup + start_localnet + ;; + stop | teardown) + stop_localnet + ;; + logs | diagnostics) + show_localnet_logs + ;; + status) + require_command curl + status_localnet + ;; + smoke | readiness) + require_command curl + run_smoke + ;; + test) + run_integration_tests + ;; + verify) + validate_configuration + require_command curl + ensure_docker_packages + start_docker_daemon + ensure_submodules + ensure_hosts_entries + quickstart_setup + start_localnet + run_smoke + run_integration_tests + ;; + *) + usage + exit 1 + ;; + esac +} + +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + main "$@" +fi diff --git a/scripts/prepare-release.ts b/scripts/prepare-release.ts new file mode 100644 index 0000000..a38763f --- /dev/null +++ b/scripts/prepare-release.ts @@ -0,0 +1,246 @@ +#!/usr/bin/env node + +/** + * Prepare Release Script + * + * Prepares a new release by selecting the next version and prepending CHANGELOG.md. + * + * Usage: npm run prepare-release + * + * Version selection (ocp-canton-sdk / ui-style floor): + * - If package.json version is ahead of npm latest (or npm returns 404) and is free, + * publish that version exactly (first publish = package.json, currently 0.1.0). + * - Otherwise patch-increment from the higher of npm latest / package.json, skipping + * versions that already exist on npm or as git tags. + * + * Rewrites package.json version in the CI workspace only — does not commit it back. + */ + +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; + +interface PackageJson { + name: string; + version: string; + [key: string]: unknown; +} + +interface ParsedVersion { + major: number; + minor: number; + patch: number; +} + +/** Check if a git tag exists */ +function tagExists(tag: string): boolean { + try { + execSync(`git rev-parse "refs/tags/${tag}"`, { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + +/** Get all published versions from NPM registry */ +function getAllNpmVersions(packageName: string): Set { + try { + const result = execSync(`npm view "${packageName}" versions --json`, { + encoding: 'utf8', + }).trim(); + const versions = JSON.parse(result) as string | string[]; + if (Array.isArray(versions)) { + return new Set(versions); + } + return new Set([versions]); + } catch { + // Package may not exist on NPM yet (404) + return new Set(); + } +} + +/** Get the latest version from NPM registry */ +function getLatestNpmVersion(packageName: string): string | null { + try { + const result = execSync(`npm view "${packageName}" version`, { encoding: 'utf8' }).trim(); + return result || null; + } catch { + // Package may not exist on NPM yet (404) + return null; + } +} + +/** Parse version string into components */ +function parseVersion(version: string): ParsedVersion | null { + const parts = version.split('.').map(Number); + if (parts.length !== 3 || parts.some(isNaN)) { + return null; + } + if (!parts.every((part) => Number.isInteger(part) && part >= 0)) { + return null; + } + return { major: parts[0]!, minor: parts[1]!, patch: parts[2]! }; +} + +/** Compare two parsed semantic versions. */ +function compareVersions(left: ParsedVersion, right: ParsedVersion): number { + if (left.major !== right.major) return left.major - right.major; + if (left.minor !== right.minor) return left.minor - right.minor; + return left.patch - right.patch; +} + +/** Find the next available version by incrementing patch until free on tags and npm */ +function findNextAvailableVersion( + isVersionTaken: (version: string) => boolean, + major: number, + minor: number, + startPatch: number +): string { + let patch = startPatch; + let version: string; + + do { + patch++; + version = `${major}.${minor}.${patch}`; + } while (isVersionTaken(version)); + + return version; +} + +/** + * Select the version to publish. + * + * A manifest version newer than the latest NPM version (including first publish when npm is + * missing) is an explicit release boundary, so publish it unchanged when it is available. + * Once that version exists, normal patch increments resume from the highest baseline. + */ +function selectReleaseVersion( + manifestVersion: string, + latestNpmVersion: string | null, + isVersionTaken: (version: string) => boolean +): string { + const manifestParsed = parseVersion(manifestVersion); + if (!manifestParsed) { + throw new Error('Invalid version format in package.json. Expected format: x.y.z'); + } + + const npmParsed = latestNpmVersion ? parseVersion(latestNpmVersion) : null; + const manifestAheadOfNpm = !npmParsed || compareVersions(manifestParsed, npmParsed) > 0; + + if (manifestAheadOfNpm && !isVersionTaken(manifestVersion)) { + return manifestVersion; + } + + const baseline = + npmParsed && compareVersions(npmParsed, manifestParsed) > 0 ? npmParsed : manifestParsed; + return findNextAvailableVersion(isVersionTaken, baseline.major, baseline.minor, baseline.patch); +} + +/** + * Prepare release by selecting version and generating changelog. + * Safe for local testing (no git tag / push operations). + */ +function prepareRelease(): void { + try { + const packageJsonPath: string = path.join(process.cwd(), 'package.json'); + const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + const packageName: string = packageJson.name; + const currentVersion: string = packageJson.version; + console.log(`Package: ${packageName}`); + console.log(`Current version in package.json: ${currentVersion}`); + + console.log('Fetching published versions from NPM...'); + const npmVersions = getAllNpmVersions(packageName); + const latestNpmVersion = getLatestNpmVersion(packageName); + + if (latestNpmVersion) { + console.log(`Latest version on NPM: ${latestNpmVersion}`); + console.log(`Total published versions: ${npmVersions.size}`); + } else { + console.log('No version found on NPM (new package or registry unavailable)'); + } + + const isVersionTaken = (version: string): boolean => + npmVersions.has(version) || tagExists(`v${version}`); + const newVersion: string = selectReleaseVersion( + currentVersion, + latestNpmVersion, + isVersionTaken + ); + + console.log(`New version: ${newVersion}`); + + packageJson.version = newVersion; + fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`); + + console.log('✅ Updated package.json with new version'); + + let commits: string; + let lastTag: string | null = null; + try { + lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', { + encoding: 'utf8', + }).trim(); + console.log(`Last tag: ${lastTag}`); + commits = execSync(`git log --oneline --format="%s" ${lastTag}..HEAD`, { + encoding: 'utf8', + }).trim(); + } catch { + // No previous tag (first publish on main): take recent history, not main..HEAD + // which is empty when HEAD is already on main. + console.log('No previous tag found, using recent commit history'); + commits = execSync('git log --oneline --format="%s" -n 20', { + encoding: 'utf8', + }).trim(); + } + + if (!commits) { + console.log('No commits found for changelog, using placeholder'); + commits = 'Initial release'; + } + + const commitLines: string[] = commits + .split('\n') + .map((commit: string): string => `- ${commit}`); + const changelog: string = commitLines.join('\n'); + + console.log('\n📋 Generated changelog:'); + console.log('='.repeat(50)); + console.log(changelog); + console.log('='.repeat(50)); + + const tagMessage = `Release v${newVersion}\n\nChanges:\n${changelog}`; + + console.log('\n🏷️ Tag message preview:'); + console.log('='.repeat(50)); + console.log(tagMessage); + console.log('='.repeat(50)); + + const changelogPath: string = path.join(process.cwd(), 'CHANGELOG.md'); + const previousVersionLink: string = lastTag + ? `\n[Previous version: ${lastTag}](https://github.com/Fairmint/canton-dev-tools/releases/tag/${lastTag})` + : ''; + + const changelogContent = `# Changelog for v${newVersion}\n\n${changelog}${previousVersionLink}\n\n`; + + if (fs.existsSync(changelogPath)) { + const existingChangelog: string = fs.readFileSync(changelogPath, 'utf8'); + fs.writeFileSync(changelogPath, changelogContent + existingChangelog); + } else { + fs.writeFileSync(changelogPath, changelogContent); + } + + console.log(`\n✅ Saved changelog to CHANGELOG.md`); + console.log(`\n🎯 Ready for release! CI will publish and tag v${newVersion}.`); + } catch (error) { + console.error('❌ Error preparing release:', (error as Error).message); + process.exit(1); + } +} + +if (require.main === module) { + prepareRelease(); +} + +export { prepareRelease, selectReleaseVersion }; diff --git a/scripts/run-cip56-transfer-smoke.ts b/scripts/run-cip56-transfer-smoke.ts new file mode 100644 index 0000000..2e675a0 --- /dev/null +++ b/scripts/run-cip56-transfer-smoke.ts @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +/** + * CIP-56 / CIP-112 LocalNet transfer smoke entrypoint. + * + * Ensures the Splice TestTokenV2 fixture DAR is present, then runs the + * integration skeleton against LocalNet Ledger JSON API (localhost:3975). + * Fails closed when Docker / LocalNet is not reachable so CI surfaces a clear error. + */ + +import { spawnSync } from 'node:child_process'; +import { join } from 'node:path'; + +const ROOT_DIR = process.cwd(); +const LEDGER_URL = process.env['FAIRMINT_TEST_LEDGER_API_URL'] ?? 'http://localhost:3975'; + +async function ledgerReachable(): Promise { + try { + const response = await fetch(`${LEDGER_URL.replace(/\/$/, '')}/v2/version`, { + signal: AbortSignal.timeout(3_000), + }); + // Match LocalNet readiness: oauth2 Ledger returns 401 without a token. + return response.ok || response.status === 401; + } catch { + return false; + } +} + +function run(command: string, args: string[]): void { + const result = spawnSync(command, args, { + cwd: ROOT_DIR, + stdio: 'inherit', + env: process.env, + }); + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +async function main(): Promise { + console.log('Fetching Splice TestTokenV2 fixture DAR (if needed)...'); + run('npx', ['tsx', 'scripts/fetch-splice-test-token-v2.ts']); + + if (!(await ledgerReachable())) { + console.error( + `LocalNet Ledger JSON API is not reachable at ${LEDGER_URL}/v2/version.\n` + + 'Start LocalNet first (`npm run localnet:start`) or run this step only inside localnet-smoke.yml.' + ); + process.exit(1); + } + + console.log('Running CIP-56 / CIP-112 transfer smoke against LocalNet...'); + run('npx', [ + 'jest', + '--runInBand', + '--testTimeout=300000', + '--testMatch', + '**/test/integration/localnet/cip56-transfer.test.ts', + join('test', 'integration', 'localnet', 'cip56-transfer.test.ts'), + ]); +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..9d78c74 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,2 @@ +/** Re-export testing helpers from the main package entry. */ +export * from './testing'; diff --git a/src/testing/cip56/accounts.ts b/src/testing/cip56/accounts.ts new file mode 100644 index 0000000..05f6ded --- /dev/null +++ b/src/testing/cip56/accounts.ts @@ -0,0 +1,113 @@ +/** + * AccountConfig / ExtraArgs JSON helpers for Splice TestTokenV2 provider-less accounts. + * + * Provider-less accounts match `TSU.basicAccount p = Account (Some p) None ""`. + * For those accounts, TestTokenV2 synthesizes AccountConfig in-choice and does not + * require AccountConfig contract ids in the choice context. + */ + +import { ACCOUNT_CONFIGS_CONTEXT_KEY, MINT_ACCOUNT_ID, TOKEN_RULES_CONTEXT_KEY } from './ids'; + +export interface Cip56PartyConfig { + canInitiate: boolean; + mustApprove: boolean; +} + +export interface Cip56Account { + owner: string | null; + provider: string | null; + id: string; +} + +export interface Cip56InstrumentId { + admin: string; + id: string; +} + +export interface Cip56AccountConfigRecord { + admin: string; + account: Cip56Account; + ownerConfig: Cip56PartyConfig; + providerConfig: Cip56PartyConfig; +} + +export interface Cip56Metadata { + values: Record; +} + +export interface Cip56ChoiceContext { + values: Record; +} + +export interface Cip56ExtraArgs { + context: Cip56ChoiceContext; + meta: Cip56Metadata; +} + +export const emptyMetadata = (): Cip56Metadata => ({ values: {} }); + +export const emptyChoiceContext = (): Cip56ChoiceContext => ({ values: {} }); + +export const emptyExtraArgs = (): Cip56ExtraArgs => ({ + context: emptyChoiceContext(), + meta: emptyMetadata(), +}); + +/** Provider-less self-custodial account (`basicAccount`). */ +export function providerLessAccount(owner: string): Cip56Account { + return { owner, provider: null, id: '' }; +} + +/** Special mint-source account (`mintAccount`). */ +export function mintAccount(): Cip56Account { + return { owner: null, provider: null, id: MINT_ACCOUNT_ID }; +} + +/** Owner can initiate and must approve; provider flags are inert when provider is null. */ +export function providerLessAccountConfig(admin: string, owner: string): Cip56AccountConfigRecord { + return { + admin, + account: providerLessAccount(owner), + ownerConfig: { canInitiate: true, mustApprove: true }, + providerConfig: { canInitiate: false, mustApprove: false }, + }; +} + +/** + * Encode ExtraArgs for provider-less flows. + * + * When `tokenRulesContractId` is set, embeds `testTokenV2/tokenRules` (required by + * TestTokenV2 EventLog lookups during transfer / mint accept). Optional AccountConfig + * contract ids go under `testTokenV2/accountConfigs` (unused for pure provider-less). + */ +export function encodeProviderLessExtraArgs( + options: { + tokenRulesContractId?: string; + accountConfigContractIds?: readonly string[]; + meta?: Record; + } = {} +): Cip56ExtraArgs { + const values: Record = {}; + + if (options.tokenRulesContractId) { + values[TOKEN_RULES_CONTEXT_KEY] = { + tag: 'AV_ContractId', + value: options.tokenRulesContractId, + }; + } + + if (options.accountConfigContractIds && options.accountConfigContractIds.length > 0) { + values[ACCOUNT_CONFIGS_CONTEXT_KEY] = { + tag: 'AV_List', + value: options.accountConfigContractIds.map((cid) => ({ + tag: 'AV_ContractId', + value: cid, + })), + }; + } + + return { + context: { values }, + meta: { values: { ...(options.meta ?? {}) } }, + }; +} diff --git a/src/testing/cip56/commands.ts b/src/testing/cip56/commands.ts new file mode 100644 index 0000000..1d250c5 --- /dev/null +++ b/src/testing/cip56/commands.ts @@ -0,0 +1,175 @@ +/** + * Unit-testable JSON command builders for the Splice TestTokenV2 CIP-56 / CIP-112 path. + * + * These produce Ledger JSON API create / exercise payloads. Orchestration (upload, vet, + * party allocation, submit) stays in scripts / integration tests. + */ + +import { + emptyExtraArgs, + mintAccount, + providerLessAccount, + providerLessAccountConfig, + type Cip56Account, + type Cip56ExtraArgs, + type Cip56InstrumentId, + type Cip56Metadata, +} from './accounts'; +import { + SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + TOKEN_RULES_TEMPLATE_ID, + TRANSFER_FACTORY_INTERFACE_ID, + TRANSFER_INSTRUCTION_INTERFACE_ID, +} from './ids'; +import { formatDamlNumeric } from './numeric'; + +export interface LedgerCreateCommand { + templateId: string; + createArgument: Record; +} + +export interface LedgerExerciseCommand { + templateId: string; + contractId: string; + choice: string; + choiceArgument: Record; +} + +export interface BuildTokenRulesCreateParams { + admin: string; +} + +/** Create TokenRules (TransferFactory + EventLog + AllocationFactory) for `admin`. */ +export function buildTokenRulesCreateCommand( + params: BuildTokenRulesCreateParams +): LedgerCreateCommand { + return { + templateId: TOKEN_RULES_TEMPLATE_ID, + createArgument: { + admin: params.admin, + }, + }; +} + +export interface BuildOfferMintParams { + tokenRulesContractId: string; + admin: string; + receiver: string; + amount: string | number; + instrumentId?: string; + offeredAt: string; + receiverConfig?: ReturnType; +} + +/** Exercise TokenRules_OfferMint (admin controller). */ +export function buildTokenRulesOfferMintCommand( + params: BuildOfferMintParams +): LedgerExerciseCommand { + const instrumentId: Cip56InstrumentId = { + admin: params.admin, + id: params.instrumentId ?? SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + }; + const receiverAccount = providerLessAccount(params.receiver); + + return { + templateId: TOKEN_RULES_TEMPLATE_ID, + contractId: params.tokenRulesContractId, + choice: 'TokenRules_OfferMint', + choiceArgument: { + receiver: receiverAccount, + amount: formatDamlNumeric(params.amount), + instrumentId, + offeredAt: params.offeredAt, + receiverConfig: + params.receiverConfig ?? providerLessAccountConfig(params.admin, params.receiver), + }, + }; +} + +export interface Cip56Transfer { + sender: Cip56Account; + receiver: Cip56Account; + amount: string; + instrumentId: Cip56InstrumentId; + inputHoldingCids: readonly string[]; + requestedAt: string; + executeBefore: string; + meta: Cip56Metadata; +} + +export interface BuildTransferFactoryTransferParams { + /** TokenRules contract id implementing TransferFactory. */ + transferFactoryContractId: string; + admin: string; + sender: string; + receiver: string; + amount: string | number; + inputHoldingCids: readonly string[]; + requestedAt: string; + executeBefore: string; + actors?: readonly string[]; + instrumentId?: string; + meta?: Record; + extraArgs?: Cip56ExtraArgs; + /** Exercise via the V2 TransferFactory interface id (default) or the TokenRules template id. */ + useInterfaceId?: boolean; +} + +/** Build CIP-112 V2 TransferFactory_Transfer choice args / exercise command. */ +export function buildTransferFactoryTransferCommand( + params: BuildTransferFactoryTransferParams +): LedgerExerciseCommand { + const transfer: Cip56Transfer = { + sender: providerLessAccount(params.sender), + receiver: providerLessAccount(params.receiver), + amount: formatDamlNumeric(params.amount), + instrumentId: { + admin: params.admin, + id: params.instrumentId ?? SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + }, + inputHoldingCids: [...params.inputHoldingCids], + requestedAt: params.requestedAt, + executeBefore: params.executeBefore, + meta: { values: { ...(params.meta ?? {}) } }, + }; + + return { + templateId: + params.useInterfaceId === false ? TOKEN_RULES_TEMPLATE_ID : TRANSFER_FACTORY_INTERFACE_ID, + contractId: params.transferFactoryContractId, + choice: 'TransferFactory_Transfer', + choiceArgument: { + transfer, + actors: [...(params.actors ?? [params.sender])], + extraArgs: params.extraArgs ?? emptyExtraArgs(), + }, + }; +} + +export interface BuildTransferInstructionAcceptParams { + transferInstructionContractId: string; + actors: readonly string[]; + extraArgs?: Cip56ExtraArgs; +} + +/** Build CIP-112 V2 TransferInstruction_Accept exercise command. */ +export function buildTransferInstructionAcceptCommand( + params: BuildTransferInstructionAcceptParams +): LedgerExerciseCommand { + return { + templateId: TRANSFER_INSTRUCTION_INTERFACE_ID, + contractId: params.transferInstructionContractId, + choice: 'TransferInstruction_Accept', + choiceArgument: { + actors: [...params.actors], + extraArgs: params.extraArgs ?? emptyExtraArgs(), + }, + }; +} + +/** Instrument `{ admin, id: 'X2' }` matching Splice TokenStandardTestEnv. */ +export function spliceTestTokenV2Instrument(admin: string): Cip56InstrumentId { + return { admin, id: SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID }; +} + +export { mintAccount, providerLessAccount, providerLessAccountConfig }; diff --git a/src/testing/cip56/fixture.ts b/src/testing/cip56/fixture.ts new file mode 100644 index 0000000..480c3bb --- /dev/null +++ b/src/testing/cip56/fixture.ts @@ -0,0 +1,39 @@ +import { createHash } from 'node:crypto'; +import { existsSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** Upstream DAR filename under `daml/dars/` in canton-network/splice. */ +export const SPLICE_TEST_TOKEN_V2_DAR_FILENAME = 'splice-test-token-v2-1.0.0.dar'; + +/** Upstream path inside the Splice repository. */ +export const SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH = `daml/dars/${SPLICE_TEST_TOKEN_V2_DAR_FILENAME}`; + +/** + * SHA-256 of `splice-test-token-v2-1.0.0.dar` at Splice `0.6.14` + * (unchanged from `0.6.11`; `fd93f86ac42ce3a08985dcd0baae530b4f235f60` era). + */ +export const SPLICE_TEST_TOKEN_V2_SHA256 = + '43fcf2fcf4e84861501a0c00e8550e2863e1aad553b1fb772ee8aa7bca7fd245'; + +/** + * Main package id embedded in the DAR (assert against Ledger package status after upload). + */ +export const SPLICE_TEST_TOKEN_V2_PACKAGE_ID = + 'a38a96b6f46c14c599b2763bc4fc68911a9cada90f89c599a1401e8e3df685e1'; + +/** Relative fixture directory inside this package (DAR is gitignored). */ +export const SPLICE_TEST_TOKEN_V2_FIXTURE_DIR = join('fixtures', 'splice-test-token-v2'); + +export function spliceTestTokenV2DarPath(packageRoot: string): string { + return join(packageRoot, SPLICE_TEST_TOKEN_V2_FIXTURE_DIR, SPLICE_TEST_TOKEN_V2_DAR_FILENAME); +} + +export function computeSha256(filePath: string): string { + return createHash('sha256').update(readFileSync(filePath)).digest('hex'); +} + +/** Returns true when the fixture DAR exists and matches {@link SPLICE_TEST_TOKEN_V2_SHA256}. */ +export function isSpliceTestTokenV2DarPresent(packageRoot: string): boolean { + const darPath = spliceTestTokenV2DarPath(packageRoot); + return existsSync(darPath) && computeSha256(darPath) === SPLICE_TEST_TOKEN_V2_SHA256; +} diff --git a/src/testing/cip56/ids.ts b/src/testing/cip56/ids.ts new file mode 100644 index 0000000..c9de7f5 --- /dev/null +++ b/src/testing/cip56/ids.ts @@ -0,0 +1,28 @@ +/** Template / interface ids for the Splice TestTokenV2 CIP-56 / CIP-112 path. */ + +export const TOKEN_RULES_TEMPLATE_ID = + '#splice-test-token-v2:Splice.Testing.Tokens.TestTokenV2:TokenRules'; + +export const TOKEN_TRANSFER_OFFER_TEMPLATE_ID = + '#splice-test-token-v2:Splice.Testing.Tokens.TestTokenV2.Transfer:TokenTransferOffer'; + +export const TRANSFER_FACTORY_INTERFACE_ID = + '#splice-api-token-transfer-instruction-v2:Splice.Api.Token.TransferInstructionV2:TransferFactory'; + +export const TRANSFER_INSTRUCTION_INTERFACE_ID = + '#splice-api-token-transfer-instruction-v2:Splice.Api.Token.TransferInstructionV2:TransferInstruction'; + +export const HOLDING_INTERFACE_ID = + '#splice-api-token-holding-v2:Splice.Api.Token.HoldingV2:Holding'; + +/** Instrument id used by Splice `TokenStandardTestEnv` (`id = "X2"`). */ +export const SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID = 'X2'; + +/** Choice-context key for AccountConfig contract ids (provider accounts). */ +export const ACCOUNT_CONFIGS_CONTEXT_KEY = 'testTokenV2/accountConfigs'; + +/** Choice-context key for the TokenRules / EventLog contract id. */ +export const TOKEN_RULES_CONTEXT_KEY = 'testTokenV2/tokenRules'; + +/** Special mint-source account id from Splice Token Standard Utils (`cip-112/mint`). */ +export const MINT_ACCOUNT_ID = 'cip-112/mint'; diff --git a/src/testing/cip56/index.ts b/src/testing/cip56/index.ts new file mode 100644 index 0000000..2e818df --- /dev/null +++ b/src/testing/cip56/index.ts @@ -0,0 +1,73 @@ +export { + ACCOUNT_CONFIGS_CONTEXT_KEY, + HOLDING_INTERFACE_ID, + MINT_ACCOUNT_ID, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + TOKEN_RULES_CONTEXT_KEY, + TOKEN_RULES_TEMPLATE_ID, + TOKEN_TRANSFER_OFFER_TEMPLATE_ID, + TRANSFER_FACTORY_INTERFACE_ID, + TRANSFER_INSTRUCTION_INTERFACE_ID, +} from './ids'; + +export { + computeSha256, + isSpliceTestTokenV2DarPresent, + SPLICE_TEST_TOKEN_V2_DAR_FILENAME, + SPLICE_TEST_TOKEN_V2_FIXTURE_DIR, + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH, + spliceTestTokenV2DarPath, +} from './fixture'; + +export { + emptyChoiceContext, + emptyExtraArgs, + emptyMetadata, + encodeProviderLessExtraArgs, + mintAccount, + providerLessAccount, + providerLessAccountConfig, + type Cip56Account, + type Cip56AccountConfigRecord, + type Cip56ChoiceContext, + type Cip56ExtraArgs, + type Cip56InstrumentId, + type Cip56Metadata, + type Cip56PartyConfig, +} from './accounts'; + +export { + buildTokenRulesCreateCommand, + buildTokenRulesOfferMintCommand, + buildTransferFactoryTransferCommand, + buildTransferInstructionAcceptCommand, + spliceTestTokenV2Instrument, + type BuildOfferMintParams, + type BuildTokenRulesCreateParams, + type BuildTransferFactoryTransferParams, + type BuildTransferInstructionAcceptParams, + type Cip56Transfer, + type LedgerCreateCommand, + type LedgerExerciseCommand, +} from './commands'; + +export { + formatDamlNumeric, + formatDamlNumericFromBaseUnits, + parseDamlNumericToBaseUnits, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS, + subtractDamlNumeric, + sumDamlNumeric, +} from './numeric'; + +export { + parseTransferInstructionResult, + runCip56TransferSmoke, + toCreateCommand, + toExerciseCommand, + type Cip56TransferSmokeOptions, + type Cip56TransferSmokeResult, + type TransferInstructionResultParsed, +} from './transferSmoke'; diff --git a/src/testing/cip56/numeric.ts b/src/testing/cip56/numeric.ts new file mode 100644 index 0000000..a65ee31 --- /dev/null +++ b/src/testing/cip56/numeric.ts @@ -0,0 +1,120 @@ +/** + * Decimal-safe helpers for Daml Numeric / Decimal JSON strings. + * + * Ledger JSON rejects scientific notation (`1e-7`). Prefer these helpers over + * `String(number)` / IEEE-754 float compares when talking to the Ledger. + */ + +/** Instrument decimals for Splice TestTokenV2 holdings (DAML Decimal precision). */ +export const SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS = 10; + +const DAML_NUMERIC_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/; + +function assertScale(scale: number): number { + if (!Number.isInteger(scale) || scale < 0 || scale > 37) { + throw new Error(`Invalid Daml Numeric scale: ${scale}`); + } + return scale; +} + +/** + * Parse a decimal string into signed base units for the given scale. + * Rejects scientific notation and excess fractional precision. + */ +export function parseDamlNumericToBaseUnits( + amount: string, + scale: number = SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS +): bigint { + assertScale(scale); + const trimmed = amount.trim(); + if (!DAML_NUMERIC_PATTERN.test(trimmed)) { + throw new Error( + `Invalid Daml Numeric string (scientific notation and non-decimal forms rejected): ${JSON.stringify(amount)}` + ); + } + + const negative = trimmed.startsWith('-'); + const unsigned = negative ? trimmed.slice(1) : trimmed; + const [wholeRaw, fractionRaw = ''] = unsigned.split('.'); + if (fractionRaw.length > scale) { + throw new Error( + `Daml Numeric ${JSON.stringify(amount)} has more than ${scale} fractional digits` + ); + } + + const whole = wholeRaw === '' ? '0' : wholeRaw; + const fraction = fractionRaw.padEnd(scale, '0'); + const units = BigInt(`${whole}${fraction}` || '0'); + return negative ? -units : units; +} + +/** Format signed base units as a Daml Numeric JSON string (never scientific notation). */ +export function formatDamlNumericFromBaseUnits( + units: bigint, + scale: number = SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS +): string { + assertScale(scale); + const negative = units < 0n; + const absolute = negative ? -units : units; + const digits = absolute.toString().padStart(scale + 1, '0'); + const whole = scale === 0 ? digits : digits.slice(0, -scale); + const fractionRaw = scale === 0 ? '' : digits.slice(-scale); + const fraction = fractionRaw.replace(/0+$/, ''); + const body = fraction.length > 0 ? `${whole}.${fraction}` : whole; + return negative ? `-${body}` : body; +} + +/** + * Normalize `string | number` into a Daml Numeric JSON string. + * Numbers are converted via fixed-point base units (no `String(n)` / `1e-7`). + */ +export function formatDamlNumeric( + amount: string | number, + scale: number = SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS +): string { + assertScale(scale); + + if (typeof amount === 'string') { + const units = parseDamlNumericToBaseUnits(amount, scale); + return formatDamlNumericFromBaseUnits(units, scale); + } + + if (typeof amount !== 'number' || !Number.isFinite(amount)) { + throw new Error(`Invalid Daml Numeric amount: ${String(amount)}`); + } + + // toFixed never emits scientific notation, but it rounds. Reject inputs that are not + // exactly representable at `scale` so command builders cannot silently mint/transfer 0. + const fixed = amount.toFixed(scale); + if (Number(fixed) !== amount) { + throw new Error( + `Number ${String(amount)} cannot be represented exactly as a Daml Numeric at scale ${scale}; pass a decimal string instead` + ); + } + const units = parseDamlNumericToBaseUnits(fixed, scale); + return formatDamlNumericFromBaseUnits(units, scale); +} + +/** Exact decimal subtraction in instrument base units, returned as a Daml Numeric string. */ +export function subtractDamlNumeric( + left: string, + right: string, + scale: number = SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS +): string { + return formatDamlNumericFromBaseUnits( + parseDamlNumericToBaseUnits(left, scale) - parseDamlNumericToBaseUnits(right, scale), + scale + ); +} + +/** Sum decimal amount strings in instrument base units. */ +export function sumDamlNumeric( + amounts: readonly string[], + scale: number = SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS +): string { + const total = amounts.reduce( + (acc, amount) => acc + parseDamlNumericToBaseUnits(amount, scale), + 0n + ); + return formatDamlNumericFromBaseUnits(total, scale); +} diff --git a/src/testing/cip56/transferSmoke.ts b/src/testing/cip56/transferSmoke.ts new file mode 100644 index 0000000..3c06cf1 --- /dev/null +++ b/src/testing/cip56/transferSmoke.ts @@ -0,0 +1,566 @@ +/** + * LocalNet CIP-56 / CIP-112 transfer smoke orchestration (Splice TestTokenV2 only). + * + * Sequence: validate DAR → upload/vet → create parties → TokenRules → OfferMint → + * Accept → TransferFactory_Transfer → Accept → assert holdings. + */ + +import { readFileSync } from 'node:fs'; + +import { + createParty, + listTokenStandardV2Holdings, + type Command, + type LedgerJsonApiClient, + type ValidatorApiClient, +} from '@fairmint/canton-node-sdk'; + +import { generateTestId, sleep } from '../testConfig'; +import { + getLocalnetParticipantAdminLedgerClient, + getLocalnetValidatorClient, +} from '../localnetLedgerClients'; +import { + buildDisclosedContract, + findCreatedContractId, + findExerciseResult, + type LedgerDisclosedContract, +} from '../transactionHelpers'; +import { + buildTokenRulesCreateCommand, + buildTokenRulesOfferMintCommand, + buildTransferFactoryTransferCommand, + buildTransferInstructionAcceptCommand, + type LedgerCreateCommand, + type LedgerExerciseCommand, +} from './commands'; +import { encodeProviderLessExtraArgs, providerLessAccount, type Cip56ExtraArgs } from './accounts'; +import { + computeSha256, + isSpliceTestTokenV2DarPresent, + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + spliceTestTokenV2DarPath, +} from './fixture'; +import { + HOLDING_INTERFACE_ID, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + TOKEN_RULES_TEMPLATE_ID, + TOKEN_TRANSFER_OFFER_TEMPLATE_ID, +} from './ids'; +import { + formatDamlNumeric, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS, + subtractDamlNumeric, + sumDamlNumeric, +} from './numeric'; + +export { SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS }; + +const DEFAULT_MINT_AMOUNT = '50'; +const DEFAULT_TRANSFER_AMOUNT = '10'; + +export interface Cip56TransferSmokeOptions { + readonly packageRoot?: string; + readonly mintAmount?: string; + readonly transferAmount?: string; + readonly ledger?: LedgerJsonApiClient; + readonly validator?: ValidatorApiClient; +} + +export interface Cip56TransferSmokeResult { + readonly adminPartyId: string; + readonly alicePartyId: string; + readonly bobPartyId: string; + readonly tokenRulesContractId: string; + readonly mintAmount: string; + readonly transferAmount: string; + readonly aliceBalance: string; + readonly bobBalance: string; + readonly mintOfferContractId: string; + readonly transferInstructionContractId: string; +} + +export function toCreateCommand(command: LedgerCreateCommand): Command { + return { + CreateCommand: { + templateId: command.templateId, + createArguments: command.createArgument as Command extends { + CreateCommand: { createArguments: infer A }; + } + ? A + : never, + }, + }; +} + +export function toExerciseCommand(command: LedgerExerciseCommand): Command { + return { + ExerciseCommand: { + templateId: command.templateId, + contractId: command.contractId, + choice: command.choice, + choiceArgument: command.choiceArgument as Command extends { + ExerciseCommand: { choiceArgument: infer A }; + } + ? A + : never, + }, + }; +} + +export interface TransferInstructionResultParsed { + readonly type: 'Pending' | 'Completed' | 'Failed'; + readonly transferInstructionCid?: string; + readonly receiverHoldingCids?: readonly string[]; +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function readNonEmptyString(value: unknown): string | undefined { + return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined; +} + +/** Parse CIP-112 V2 TransferInstructionResult JSON (`tag` / `value` encoding). */ +export function parseTransferInstructionResult( + exerciseResult: unknown +): TransferInstructionResultParsed { + if (!isRecord(exerciseResult)) { + throw new Error( + `TransferInstructionResult is not an object: ${JSON.stringify(exerciseResult)}` + ); + } + const output = exerciseResult['output']; + if (!isRecord(output)) { + throw new Error(`TransferInstructionResult.output missing: ${JSON.stringify(exerciseResult)}`); + } + const tag = readNonEmptyString(output['tag']); + if (!tag) { + throw new Error( + `TransferInstructionResult.output.tag missing: ${JSON.stringify(exerciseResult)}` + ); + } + + if (tag === 'TransferInstructionResult_Failed') { + return { type: 'Failed' }; + } + + const value = output['value']; + if (tag === 'TransferInstructionResult_Pending') { + const cid = + readNonEmptyString(value) ?? + (isRecord(value) ? readNonEmptyString(value['transferInstructionCid']) : undefined); + if (!cid) { + throw new Error(`Pending result missing transferInstructionCid: ${JSON.stringify(output)}`); + } + return { type: 'Pending', transferInstructionCid: cid }; + } + + if (tag === 'TransferInstructionResult_Completed') { + const variant = isRecord(value) ? value : undefined; + const cids = variant?.['receiverHoldingCids']; + if (!Array.isArray(cids) || !cids.every((c) => typeof c === 'string')) { + throw new Error(`Completed result missing receiverHoldingCids: ${JSON.stringify(output)}`); + } + return { type: 'Completed', receiverHoldingCids: cids }; + } + + throw new Error(`Unknown TransferInstructionResult tag: ${tag}`); +} + +function daysFromNowIso(days: number, fromMs: number = Date.now()): string { + return new Date(fromMs + days * 24 * 60 * 60 * 1000).toISOString(); +} + +async function grantActAsRights( + ledger: LedgerJsonApiClient, + partyIds: readonly string[] +): Promise { + const authenticated = await ledger.getAuthenticatedUser({}); + const userId = authenticated.user.id; + await ledger.grantUserRights({ + userId, + rights: partyIds.map((party) => ({ + kind: { CanActAs: { value: { party } } }, + })), + }); +} + +async function discloseTokenRules( + ledger: LedgerJsonApiClient, + params: { + tokenRulesContractId: string; + tokenRulesTemplateId: string; + synchronizerId: string; + adminPartyId: string; + } +): Promise { + return buildDisclosedContract(ledger, { + contractId: params.tokenRulesContractId, + templateId: params.tokenRulesTemplateId, + synchronizerId: params.synchronizerId, + readAsParty: params.adminPartyId, + }); +} + +async function listUnlockedHoldings( + ledger: LedgerJsonApiClient, + params: { + partyId: string; + adminPartyId: string; + } +): Promise<{ contractIds: string[]; amounts: string[]; total: string }> { + const holdings = await listTokenStandardV2Holdings({ + ledger, + parties: [params.partyId], + account: providerLessAccount(params.partyId), + instrumentId: { + admin: params.adminPartyId, + id: SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + }, + instrumentDecimals: SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS, + holdingInterfaceId: HOLDING_INTERFACE_ID, + }); + const unlocked = holdings.filter((holding) => holding.lock === null); + const contractIds = unlocked.map((holding) => holding.contractId); + const amounts = unlocked.map((holding) => holding.amount); + return { contractIds, amounts, total: sumDamlNumeric(amounts) }; +} + +/** Collect messages from an error and its nested `cause` chain. */ +export function collectErrorMessages(err: unknown, depth = 5): string { + const parts: string[] = []; + let current: unknown = err; + for (let i = 0; i < depth && current !== undefined && current !== null; i += 1) { + if (current instanceof Error) { + parts.push(current.message); + current = 'cause' in current ? (current as { cause?: unknown }).cause : undefined; + continue; + } + parts.push(String(current)); + break; + } + return parts.join('\n'); +} + +/** + * LocalNet Validator may return HTTP 429 while traffic balance is still below the + * reserved amount (`0 < 200000`). That is transient until auto top-up lands. + */ +export function isTransientValidatorTrafficError(err: unknown): boolean { + const text = collectErrorMessages(err); + return ( + /\bHTTP 429\b/i.test(text) || + /Too Many Requests/i.test(text) || + /Traffic balance below reserved traffic amount/i.test(text) + ); +} + +async function createPartyAfterTrafficReady(params: { + ledger: LedgerJsonApiClient; + validator: ValidatorApiClient; + partyName: string; + timeoutMs?: number; + pollIntervalMs?: number; +}): Promise<{ partyId: string }> { + const timeoutMs = params.timeoutMs ?? 180_000; + const pollIntervalMs = params.pollIntervalMs ?? 10_000; + const deadline = Date.now() + timeoutMs; + let lastError: Error | undefined; + let attempt = 0; + + while (Date.now() < deadline) { + attempt += 1; + // Fresh party prefix per attempt so a partial createUser cannot collide on retry. + const partyName = attempt === 1 ? params.partyName : `${params.partyName}-r${attempt}`; + try { + return await createParty({ + ledgerClient: params.ledger, + validatorClient: params.validator, + partyName, + amount: '0', + }); + } catch (err) { + lastError = err instanceof Error ? err : new Error(String(err)); + if (!isTransientValidatorTrafficError(err)) { + throw lastError; + } + const remainingMs = deadline - Date.now(); + if (remainingMs <= 0) { + break; + } + await sleep(Math.min(pollIntervalMs, remainingMs)); + } + } + + throw new Error( + `Timed out waiting for Validator traffic before createParty(${params.partyName})` + + (lastError ? `: ${lastError.message}` : '') + ); +} + +/** + * Run the full Splice TestTokenV2 CIP-56 / CIP-112 LocalNet transfer smoke. + * + * @throws When the fixture DAR is missing/mismatched or LocalNet Ledger / Validator is unreachable. + */ +export async function runCip56TransferSmoke( + options: Cip56TransferSmokeOptions = {} +): Promise { + const packageRoot = options.packageRoot ?? process.cwd(); + const mintAmount = formatDamlNumeric(options.mintAmount ?? DEFAULT_MINT_AMOUNT); + const transferAmount = formatDamlNumeric(options.transferAmount ?? DEFAULT_TRANSFER_AMOUNT); + const darPath = spliceTestTokenV2DarPath(packageRoot); + + if (!isSpliceTestTokenV2DarPresent(packageRoot)) { + const actual = (() => { + try { + return computeSha256(darPath); + } catch { + return '(missing)'; + } + })(); + throw new Error( + `Splice TestTokenV2 DAR missing or hash mismatch at ${darPath}. ` + + `Expected ${SPLICE_TEST_TOKEN_V2_SHA256}, got ${actual}. ` + + `Run: npm run fixture:splice-test-token-v2:fetch` + ); + } + + const ledger = options.ledger ?? (await getLocalnetParticipantAdminLedgerClient()); + const validator = options.validator ?? (await getLocalnetValidatorClient()); + + const darFile = readFileSync(darPath); + + // 2–3. validate + upload/vet + await ledger.validateDar({ darFile }); + await ledger.uploadDar({ darFile, vetAllPackages: true }); + + const packages = await ledger.listPackages(); + if (!packages.packageIds.includes(SPLICE_TEST_TOKEN_V2_PACKAGE_ID)) { + throw new Error( + `Uploaded DAR but package id ${SPLICE_TEST_TOKEN_V2_PACKAGE_ID} is not in listPackages()` + ); + } + const packageStatus = await ledger.getPackageStatus({ + packageId: SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + }); + if (packageStatus.packageStatus !== 'PACKAGE_STATUS_REGISTERED') { + throw new Error( + `Expected PACKAGE_STATUS_REGISTERED for ${SPLICE_TEST_TOKEN_V2_PACKAGE_ID}, got ${JSON.stringify(packageStatus)}` + ); + } + + // 4. parties (amount 0 — identity only, no amulet funding). + // Retry through transient Validator traffic-balance 429s after LocalNet bootstrap. + const runId = generateTestId('cip56'); + const { partyId: adminPartyId } = await createPartyAfterTrafficReady({ + ledger, + validator, + partyName: `${runId}-admin`, + }); + const { partyId: alicePartyId } = await createPartyAfterTrafficReady({ + ledger, + validator, + partyName: `${runId}-alice`, + }); + const { partyId: bobPartyId } = await createPartyAfterTrafficReady({ + ledger, + validator, + partyName: `${runId}-bob`, + }); + + await grantActAsRights(ledger, [adminPartyId, alicePartyId, bobPartyId]); + ledger.setPartyId(adminPartyId); + + // 5. TokenRules + const createTree = await ledger.submitAndWaitForTransactionTree({ + actAs: [adminPartyId], + commands: [toCreateCommand(buildTokenRulesCreateCommand({ admin: adminPartyId }))], + }); + const tokenRulesContractId = findCreatedContractId(createTree, 'TokenRules'); + const tokenRulesEvents = await ledger.getEventsByContractId({ + contractId: tokenRulesContractId, + readAs: [adminPartyId], + }); + const synchronizerId = tokenRulesEvents.created?.synchronizerId; + if (!synchronizerId) { + throw new Error(`Could not resolve synchronizerId for TokenRules ${tokenRulesContractId}`); + } + // Prefer the package-name template id for ACS filters (Ledger rejects raw package-id refs). + const tokenRulesTemplateId = TOKEN_RULES_TEMPLATE_ID; + + const tokenRulesDisclosure = await discloseTokenRules(ledger, { + tokenRulesContractId, + tokenRulesTemplateId, + synchronizerId, + adminPartyId, + }); + const extraArgs: Cip56ExtraArgs = encodeProviderLessExtraArgs({ + tokenRulesContractId, + }); + + // 6. mint — OfferMint (admin) then Accept (alice) + // offeredAt must be <= ledger time (assertDeadlineExceeded). + const offeredAt = new Date(Date.now() - 60_000).toISOString(); + const offerMintTree = await ledger.submitAndWaitForTransactionTree({ + actAs: [adminPartyId], + commands: [ + toExerciseCommand( + buildTokenRulesOfferMintCommand({ + tokenRulesContractId, + admin: adminPartyId, + receiver: alicePartyId, + amount: mintAmount, + offeredAt, + }) + ), + ], + }); + + const offerMintResult = findExerciseResult<{ offerCid?: string }>( + offerMintTree, + 'TokenRules_OfferMint' + ); + const mintOfferContractId = + (typeof offerMintResult?.offerCid === 'string' && offerMintResult.offerCid) || + findCreatedContractId(offerMintTree, 'TokenTransferOffer'); + + const mintAcceptTree = await ledger.submitAndWaitForTransactionTree({ + actAs: [alicePartyId], + disclosedContracts: [tokenRulesDisclosure], + commands: [ + toExerciseCommand( + buildTransferInstructionAcceptCommand({ + transferInstructionContractId: mintOfferContractId, + actors: [alicePartyId], + extraArgs, + }) + ), + ], + }); + const mintAcceptParsed = parseTransferInstructionResult( + findExerciseResult(mintAcceptTree, 'TransferInstruction_Accept') + ); + if (mintAcceptParsed.type !== 'Completed') { + throw new Error( + `Expected mint Accept to Complete, got ${mintAcceptParsed.type}: ${JSON.stringify(mintAcceptParsed)}` + ); + } + + const aliceAfterMint = await listUnlockedHoldings(ledger, { + partyId: alicePartyId, + adminPartyId, + }); + if (aliceAfterMint.total !== mintAmount) { + throw new Error( + `Alice balance after mint expected ${mintAmount}, got ${aliceAfterMint.total} (${aliceAfterMint.amounts.join(',')})` + ); + } + if (aliceAfterMint.contractIds.length === 0) { + throw new Error('Alice has no unlocked holdings after mint Accept'); + } + + // 7. transfer — TransferFactory_Transfer (alice) then Accept (bob) + const requestedAt = new Date(Date.now() - 1_000).toISOString(); + const executeBefore = daysFromNowIso(7); + const transferTree = await ledger.submitAndWaitForTransactionTree({ + actAs: [alicePartyId], + disclosedContracts: [tokenRulesDisclosure], + commands: [ + toExerciseCommand( + buildTransferFactoryTransferCommand({ + transferFactoryContractId: tokenRulesContractId, + admin: adminPartyId, + sender: alicePartyId, + receiver: bobPartyId, + amount: transferAmount, + inputHoldingCids: aliceAfterMint.contractIds, + requestedAt, + executeBefore, + actors: [alicePartyId], + extraArgs, + }) + ), + ], + }); + + const transferExerciseResult = findExerciseResult(transferTree, 'TransferFactory_Transfer'); + const transferParsed = parseTransferInstructionResult(transferExerciseResult); + if (transferParsed.type !== 'Pending' || !transferParsed.transferInstructionCid) { + throw new Error( + `Expected TransferFactory_Transfer Pending instruction, got ${JSON.stringify(transferParsed)} ` + + `(raw=${JSON.stringify(transferExerciseResult)})` + ); + } + const transferInstructionContractId = transferParsed.transferInstructionCid; + + // Disclose the pending offer + TokenRules. Also readAs alice so the instruction is + // visible on this participant even if bob's observer projection lags. + const pendingDisclosure = await buildDisclosedContract(ledger, { + contractId: transferInstructionContractId, + templateId: TOKEN_TRANSFER_OFFER_TEMPLATE_ID, + synchronizerId, + readAsParty: alicePartyId, + }); + + const acceptTransferTree = await ledger.submitAndWaitForTransactionTree({ + actAs: [bobPartyId], + readAs: [alicePartyId, adminPartyId], + disclosedContracts: [tokenRulesDisclosure, pendingDisclosure], + commands: [ + toExerciseCommand( + buildTransferInstructionAcceptCommand({ + transferInstructionContractId, + actors: [bobPartyId], + extraArgs, + }) + ), + ], + }); + const bobAcceptParsed = parseTransferInstructionResult( + findExerciseResult(acceptTransferTree, 'TransferInstruction_Accept') + ); + if (bobAcceptParsed.type !== 'Completed') { + throw new Error( + `Expected bob Accept to Complete, got ${bobAcceptParsed.type}: ${JSON.stringify(bobAcceptParsed)}` + ); + } + + // 8. assert holdings + const aliceAfter = await listUnlockedHoldings(ledger, { + partyId: alicePartyId, + adminPartyId, + }); + const bobAfter = await listUnlockedHoldings(ledger, { + partyId: bobPartyId, + adminPartyId, + }); + + const expectedAlice = subtractDamlNumeric(mintAmount, transferAmount); + const expectedBob = formatDamlNumeric(transferAmount); + + if (aliceAfter.total !== expectedAlice) { + throw new Error( + `Alice balance after transfer expected ${expectedAlice}, got ${aliceAfter.total}` + ); + } + if (bobAfter.total !== expectedBob) { + throw new Error(`Bob balance after transfer expected ${expectedBob}, got ${bobAfter.total}`); + } + + return { + adminPartyId, + alicePartyId, + bobPartyId, + tokenRulesContractId, + mintAmount, + transferAmount, + aliceBalance: aliceAfter.total, + bobBalance: bobAfter.total, + mintOfferContractId, + transferInstructionContractId, + }; +} diff --git a/src/testing/index.ts b/src/testing/index.ts new file mode 100644 index 0000000..71ddee9 --- /dev/null +++ b/src/testing/index.ts @@ -0,0 +1,82 @@ +export { + buildIntegrationTestClientConfig, + buildLocalnetClientConfig, + createSharedSecretJwt, + generateTestId, + retry, + sleep, +} from './testConfig'; +export { + getLocalnetNonAdminLedgerClient, + getLocalnetParticipantAdminLedgerClient, + getLocalnetValidatorClient, +} from './localnetLedgerClients'; +export { + buildDisclosedContract, + fetchCreatedEventBlob, + findAllCreatedContractIds, + findCreatedContractId, + findExerciseResult, + listCreatedEvents, + listExercisedEvents, + type LedgerDisclosedContract, +} from './transactionHelpers'; + +export { + ACCOUNT_CONFIGS_CONTEXT_KEY, + HOLDING_INTERFACE_ID, + MINT_ACCOUNT_ID, + SPLICE_TEST_TOKEN_V2_DAR_FILENAME, + SPLICE_TEST_TOKEN_V2_FIXTURE_DIR, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + SPLICE_TEST_TOKEN_V2_UPSTREAM_PATH, + TOKEN_RULES_CONTEXT_KEY, + TOKEN_RULES_TEMPLATE_ID, + TOKEN_TRANSFER_OFFER_TEMPLATE_ID, + TRANSFER_FACTORY_INTERFACE_ID, + TRANSFER_INSTRUCTION_INTERFACE_ID, + buildTokenRulesCreateCommand, + buildTokenRulesOfferMintCommand, + buildTransferFactoryTransferCommand, + buildTransferInstructionAcceptCommand, + computeSha256, + emptyChoiceContext, + emptyExtraArgs, + emptyMetadata, + encodeProviderLessExtraArgs, + isSpliceTestTokenV2DarPresent, + mintAccount, + providerLessAccount, + providerLessAccountConfig, + spliceTestTokenV2DarPath, + spliceTestTokenV2Instrument, + parseTransferInstructionResult, + runCip56TransferSmoke, + toCreateCommand, + toExerciseCommand, + formatDamlNumeric, + formatDamlNumericFromBaseUnits, + parseDamlNumericToBaseUnits, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_DECIMALS, + subtractDamlNumeric, + sumDamlNumeric, + type BuildOfferMintParams, + type BuildTokenRulesCreateParams, + type BuildTransferFactoryTransferParams, + type BuildTransferInstructionAcceptParams, + type Cip56Account, + type Cip56AccountConfigRecord, + type Cip56ChoiceContext, + type Cip56ExtraArgs, + type Cip56InstrumentId, + type Cip56Metadata, + type Cip56PartyConfig, + type Cip56Transfer, + type Cip56TransferSmokeOptions, + type Cip56TransferSmokeResult, + type LedgerCreateCommand, + type LedgerExerciseCommand, + type TransferInstructionResultParsed, +} from './cip56'; diff --git a/src/testing/localnetLedgerClients.ts b/src/testing/localnetLedgerClients.ts new file mode 100644 index 0000000..bb820a0 --- /dev/null +++ b/src/testing/localnetLedgerClients.ts @@ -0,0 +1,229 @@ +/** Role-checked Ledger JSON API clients for either cn-quickstart LocalNet authentication mode. */ + +import { CantonRuntime, LedgerJsonApiClient, ValidatorApiClient } from '@fairmint/canton-node-sdk'; +import { createSharedSecretJwt } from './testConfig'; + +function getEnv(name: string): string | undefined { + const value = process.env[name]; + return value && value.length > 0 ? value : undefined; +} + +const LEDGER_API_URL = getEnv('FAIRMINT_TEST_LEDGER_API_URL') ?? 'http://localhost:3975'; +const VALIDATOR_API_URL = getEnv('FAIRMINT_TEST_VALIDATOR_API_URL') ?? 'http://localhost:3903'; +const OAUTH_TOKEN_URL = + getEnv('FAIRMINT_TEST_AUTH_URL') ?? + 'http://localhost:8082/realms/AppProvider/protocol/openid-connect/token'; + +let participantAdminClientPromise: Promise | undefined; +let nonAdminClientPromise: Promise | undefined; +let validatorClientPromise: Promise | undefined; + +function memoizeClient( + read: () => Promise | undefined, + write: (value: Promise | undefined) => void, + create: () => Promise +): Promise { + const existing = read(); + if (existing) { + return existing; + } + const created = create().catch((error: unknown) => { + write(undefined); + throw error; + }); + write(created); + return created; +} + +/** Resolve a participant-admin client without assuming LocalNet auth mode. */ +export async function getLocalnetParticipantAdminLedgerClient(): Promise { + return memoizeClient( + () => participantAdminClientPromise, + (value) => { + participantAdminClientPromise = value; + }, + () => + resolveRoleCheckedClient( + [createSharedSecretClient('ledger-api-user'), createOAuthAdminClient()], + true + ) + ); +} + +/** Resolve an authenticated client verified not to hold the ParticipantAdmin right. */ +export async function getLocalnetNonAdminLedgerClient(): Promise { + return memoizeClient( + () => nonAdminClientPromise, + (value) => { + nonAdminClientPromise = value; + }, + () => + resolveRoleCheckedClient( + [createSharedSecretClient('app-provider'), createOAuthNonAdminClient()], + false + ) + ); +} + +async function resolveRoleCheckedClient( + candidates: readonly LedgerJsonApiClient[], + requireParticipantAdmin: boolean +): Promise { + let lastError: unknown; + + for (const candidate of candidates) { + let authenticated; + try { + authenticated = await candidate.getAuthenticatedUser({}); + } catch (error) { + lastError = error; + continue; + } + + const rights = await candidate.listUserRights({ userId: authenticated.user.id }); + const hasParticipantAdmin = + rights.rights?.some( + (right) => right.kind !== undefined && 'ParticipantAdmin' in right.kind + ) ?? false; + if (hasParticipantAdmin !== requireParticipantAdmin) { + lastError = new Error( + `LocalNet fixture ${authenticated.user.id} ${ + requireParticipantAdmin ? 'does not have' : 'unexpectedly has' + } ParticipantAdmin` + ); + continue; + } + return candidate; + } + + if (lastError instanceof Error) { + throw lastError; + } + throw new Error('Could not authenticate a role-checked LocalNet Ledger client'); +} + +/** + * Resolve a Validator API client for LocalNet party onboarding (`createParty`). + * + * Prefers OAuth2 (package LocalNet default). Shared-secret is tried second for + * consumer CI that runs LocalNet without Keycloak; note that some validator + * admin endpoints only accept OAuth tokens even when getValidatorUserInfo works + * with a shared-secret JWT. + */ +export async function getLocalnetValidatorClient(): Promise { + return memoizeClient( + () => validatorClientPromise, + (value) => { + validatorClientPromise = value; + }, + () => + resolveValidatorClient([ + createOAuthValidatorClient(), + createSharedSecretValidatorClient('ledger-api-user'), + ]) + ); +} + +async function resolveValidatorClient( + candidates: readonly ValidatorApiClient[] +): Promise { + let lastAuthenticationError: unknown; + + for (const candidate of candidates) { + try { + await candidate.getValidatorUserInfo(); + return candidate; + } catch (error) { + lastAuthenticationError = error; + } + } + + if (lastAuthenticationError instanceof Error) { + throw lastAuthenticationError; + } + throw new Error('Could not authenticate a LocalNet Validator API client'); +} + +function createOAuthAdminClient(): LedgerJsonApiClient { + // SDK LocalNet OAuth defaults for ParticipantAdmin (cn-quickstart app-provider). + return new LedgerJsonApiClient( + new CantonRuntime({ network: 'localnet', provider: 'app-provider' }) + ); +} + +function createOAuthNonAdminClient(): LedgerJsonApiClient { + return new LedgerJsonApiClient( + new CantonRuntime({ + network: 'localnet', + provider: 'app-provider', + authUrl: OAUTH_TOKEN_URL, + apis: { + LEDGER_JSON_API: { + apiUrl: LEDGER_API_URL, + auth: { + grantType: 'password', + clientId: 'app-provider-unsafe', + username: 'app-provider', + password: 'abc123', + scope: 'openid', + }, + }, + }, + }) + ); +} + +function createOAuthValidatorClient(): ValidatorApiClient { + return new ValidatorApiClient( + new CantonRuntime({ network: 'localnet', provider: 'app-provider' }) + ); +} + +function createSharedSecretClient(subject: string): LedgerJsonApiClient { + return new LedgerJsonApiClient( + new CantonRuntime({ + network: 'localnet', + provider: 'app-provider', + authUrl: '', + apis: { + LEDGER_JSON_API: { + apiUrl: LEDGER_API_URL, + auth: { + grantType: 'client_credentials', + clientId: `shared-secret-${subject}`, + bearerToken: createSharedSecretJwt({ subject }), + }, + }, + }, + }) + ); +} + +function createSharedSecretValidatorClient(subject: string): ValidatorApiClient { + const bearerToken = createSharedSecretJwt({ subject }); + return new ValidatorApiClient( + new CantonRuntime({ + network: 'localnet', + provider: 'app-provider', + authUrl: '', + apis: { + LEDGER_JSON_API: { + apiUrl: LEDGER_API_URL, + auth: { + grantType: 'client_credentials', + clientId: `shared-secret-${subject}`, + bearerToken, + }, + }, + VALIDATOR_API: { + apiUrl: VALIDATOR_API_URL, + auth: { + grantType: 'client_credentials', + clientId: `shared-secret-${subject}`, + bearerToken, + }, + }, + }, + }) + ); +} diff --git a/src/testing/testConfig.ts b/src/testing/testConfig.ts new file mode 100644 index 0000000..ba94b49 --- /dev/null +++ b/src/testing/testConfig.ts @@ -0,0 +1,173 @@ +/** + * Shared LocalNet client configuration and async helpers for Canton integration tests. + */ + +import { createHmac } from 'node:crypto'; +import type { AuthConfig, ClientConfig } from '@fairmint/canton-node-sdk'; + +function getEnv(name: string): string | undefined { + const value = process.env[name]; + return value && value.length > 0 ? value : undefined; +} + +export interface SharedSecretJwtOptions { + subject?: string; + secret?: string; + audience?: string; +} + +/** Create an HS256 JWT for cn-quickstart shared-secret LocalNet authentication. */ +export function createSharedSecretJwt(options: SharedSecretJwtOptions = {}): string { + const subject = options.subject ?? getEnv('FAIRMINT_TEST_USER_ID') ?? 'ledger-api-user'; + const secret = options.secret ?? getEnv('FAIRMINT_TEST_SHARED_SECRET') ?? 'unsafe'; + const audience = options.audience ?? 'https://canton.network.global'; + + const encode = (value: object): string => + Buffer.from(JSON.stringify(value)).toString('base64url'); + const header = encode({ alg: 'HS256', typ: 'JWT' }); + const payload = encode({ sub: subject, aud: audience }); + const unsignedToken = `${header}.${payload}`; + const signature = createHmac('sha256', secret).update(unsignedToken).digest('base64url'); + return `${unsignedToken}.${signature}`; +} + +/** Stable id generator for test data (avoid collisions across runs). */ +export function generateTestId(prefix: string): string { + return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; +} + +/** + * Build a {@link ClientConfig} for LocalNet integration tests. + * + * Defaults match cn-quickstart's app-provider participant with OAuth2 when no auth overrides are set. + * Set `FAIRMINT_TEST_AUTH_URL` / shared-secret env vars to switch modes. + */ +export function buildLocalnetClientConfig(): ClientConfig { + const authUrl = getEnv('FAIRMINT_TEST_AUTH_URL'); + const network = (getEnv('FAIRMINT_TEST_NETWORK') ?? 'localnet') as ClientConfig['network']; + const ledgerApiUrl = getEnv('FAIRMINT_TEST_LEDGER_API_URL'); + const validatorApiUrl = getEnv('FAIRMINT_TEST_VALIDATOR_API_URL'); + const scanApiUrl = getEnv('FAIRMINT_TEST_SCAN_API_URL'); + const hasAuthOverride = + Boolean(authUrl) || + Boolean(getEnv('FAIRMINT_TEST_CLIENT_ID')) || + Boolean(getEnv('FAIRMINT_TEST_SHARED_SECRET')); + const hasEndpointOverride = + Boolean(ledgerApiUrl) || Boolean(validatorApiUrl) || Boolean(scanApiUrl); + + if (!hasAuthOverride && !hasEndpointOverride) { + // SDK built-in LocalNet OAuth2 defaults (cn-quickstart option 2). + return { + network, + provider: 'app-provider', + }; + } + + // URL-only overrides keep package OAuth2 defaults (Keycloak password grant). + const resolvedAuthUrl = + authUrl ?? + (!hasAuthOverride + ? 'http://localhost:8082/realms/AppProvider/protocol/openid-connect/token' + : undefined); + + return { + network, + provider: 'app-provider', + ...(resolvedAuthUrl ? { authUrl: resolvedAuthUrl } : {}), + apis: { + LEDGER_JSON_API: { + apiUrl: ledgerApiUrl ?? 'http://localhost:3975', + auth: buildAuthConfig('LEDGER_JSON_API', { preferOAuth: !hasAuthOverride }), + }, + VALIDATOR_API: { + apiUrl: validatorApiUrl ?? 'http://localhost:3903', + auth: buildAuthConfig('VALIDATOR_API', { preferOAuth: !hasAuthOverride }), + }, + SCAN_API: { + apiUrl: scanApiUrl ?? 'http://localhost:4000', + auth: buildAuthConfig('SCAN_API', { preferOAuth: !hasAuthOverride }), + }, + }, + }; +} + +/** @deprecated Prefer {@link buildLocalnetClientConfig}. */ +export const buildIntegrationTestClientConfig = buildLocalnetClientConfig; + +function buildAuthConfig( + apiType: string, + options: { preferOAuth?: boolean } = {} +): AuthConfig { + const apiPrefix = `FAIRMINT_TEST_${apiType}`; + const authUrl = getEnv('FAIRMINT_TEST_AUTH_URL'); + const clientId = + getEnv(`${apiPrefix}_CLIENT_ID`) ?? getEnv('FAIRMINT_TEST_CLIENT_ID') ?? 'fairmint-sdk'; + const clientSecret = + getEnv(`${apiPrefix}_CLIENT_SECRET`) ?? getEnv('FAIRMINT_TEST_CLIENT_SECRET'); + + if (authUrl) { + return { + grantType: 'client_credentials', + clientId, + ...(clientSecret ? { clientSecret } : {}), + }; + } + + if (options.preferOAuth) { + // cn-quickstart LocalNet OAuth2 (app-provider-unsafe password grant). + return { + grantType: 'password', + clientId: 'app-provider-unsafe', + username: 'app-provider', + password: 'abc123', + scope: 'openid', + }; + } + + return { + grantType: 'client_credentials', + clientId, + bearerToken: createSharedSecretJwt(), + }; +} + +export async function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * Retry a function until it succeeds or times out. + * + * @throws The last error if all retries fail. + */ +export async function retry( + fn: () => Promise, + options: { + timeoutMs?: number; + pollIntervalMs?: number; + description?: string; + } = {} +): Promise { + const timeoutMs = options.timeoutMs ?? 30_000; + const pollIntervalMs = options.pollIntervalMs ?? 1_000; + const description = options.description ?? 'operation'; + const deadline = Date.now() + timeoutMs; + let lastError: Error | undefined; + + while (Date.now() < deadline) { + try { + return await fn(); + } catch (err) { + lastError = err instanceof Error ? err : new Error(String(err)); + const remainingMs = deadline - Date.now(); + if (remainingMs <= 0) { + break; + } + await sleep(Math.min(pollIntervalMs, remainingMs)); + } + } + + throw new Error( + `Timed out waiting for ${description}${lastError ? `: ${lastError.message}` : ''}` + ); +} diff --git a/src/testing/transactionHelpers.ts b/src/testing/transactionHelpers.ts new file mode 100644 index 0000000..8d30e1d --- /dev/null +++ b/src/testing/transactionHelpers.ts @@ -0,0 +1,192 @@ +/** + * Helpers for inspecting `submitAndWaitForTransactionTree` results in Canton integration tests. + */ + +import { + extractEventsFromTransaction, + type LedgerJsonApiClient, + type ParsedCreatedEvent, + type ParsedExercisedEvent, +} from '@fairmint/canton-node-sdk'; + +type CreatedTreeEventValue = Pick< + ParsedCreatedEvent, + 'contractId' | 'templateId' | 'createdEventBlob' | 'createArgument' +>; +type ExercisedTreeEventValue = Pick< + ParsedExercisedEvent, + 'contractId' | 'templateId' | 'choice' | 'exerciseResult' +>; + +/** Return all `CreatedTreeEvent` payloads in document order. */ +export function listCreatedEvents(input: unknown): CreatedTreeEventValue[] { + return extractEventsFromTransaction(input).created; +} + +/** Return all `ExercisedTreeEvent` payloads in document order. */ +export function listExercisedEvents(input: unknown): ExercisedTreeEventValue[] { + return extractEventsFromTransaction(input).exercised; +} + +/** + * Find the first created contract whose `templateId` contains `templateNameSubstring`. + * + * @throws If no matching event is found. + */ +export function findCreatedContractId(input: unknown, templateNameSubstring: string): string { + for (const created of listCreatedEvents(input)) { + if (created.templateId.includes(templateNameSubstring)) { + return created.contractId; + } + } + throw new Error( + `No CreatedTreeEvent with templateId containing "${templateNameSubstring}"; created templates: ` + + `${listCreatedEvents(input) + .map((c) => c.templateId) + .join(', ')}` + ); +} + +/** All created contract IDs whose `templateId` contains `templateNameSubstring`. */ +export function findAllCreatedContractIds(input: unknown, templateNameSubstring: string): string[] { + return listCreatedEvents(input) + .filter((c) => c.templateId.includes(templateNameSubstring)) + .map((c) => c.contractId); +} + +/** + * Find the first exercise event matching `choiceName` and return its `exerciseResult`. + * + * @throws If no matching event is found. + */ +export function findExerciseResult(input: unknown, choiceName: string): T | undefined { + for (const exercised of listExercisedEvents(input)) { + if (exercised.choice === choiceName) { + return exercised.exerciseResult as T | undefined; + } + } + throw new Error( + `No ExercisedTreeEvent with choice "${choiceName}"; exercised choices: ` + + `${listExercisedEvents(input) + .map((c) => c.choice) + .join(', ')}` + ); +} + +/** Fetch the active createdEventBlob for a contract. */ +export async function fetchCreatedEventBlob( + ledger: LedgerJsonApiClient, + contractId: string, + readAs?: string[] +): Promise { + const events = await ledger.getEventsByContractId({ + contractId, + ...(readAs?.length ? { readAs } : {}), + }); + const blob = events.created?.createdEvent.createdEventBlob; + if (!blob) { + throw new Error(`No createdEventBlob found for contract ${contractId}`); + } + return blob; +} + +async function fetchCreatedEventDisclosure( + ledger: LedgerJsonApiClient, + contractId: string, + readAs?: string[] +): Promise { + const events = await ledger.getEventsByContractId({ + contractId, + ...(readAs?.length ? { readAs } : {}), + }); + const created = events.created?.createdEvent; + const blob = created?.createdEventBlob; + if (!created || !blob) { + throw new Error(`No createdEventBlob found for contract ${contractId}`); + } + return { + templateId: created.templateId, + contractId: created.contractId, + createdEventBlob: blob, + synchronizerId: events.created?.synchronizerId ?? '', + }; +} + +/** Disclosed contract payload for JSON API command submission. */ +export interface LedgerDisclosedContract { + templateId: string; + contractId: string; + createdEventBlob: string; + synchronizerId: string; +} + +function requireTemplateIdMatch(params: { + contractId: string; + actualTemplateId: string; + expectedTemplateId: string; +}): void { + if ( + params.actualTemplateId === params.expectedTemplateId || + templateDescriptor(params.actualTemplateId) === templateDescriptor(params.expectedTemplateId) + ) { + return; + } + throw new Error( + `Contract ${params.contractId} has templateId ${params.actualTemplateId}; expected ${params.expectedTemplateId}` + ); +} + +function templateDescriptor(templateId: string): string { + return templateId.split(':').slice(1).join(':') || templateId; +} + +/** Build a disclosed contract for an exercise on a contract visible to another party. */ +export async function buildDisclosedContract( + ledger: LedgerJsonApiClient, + params: { + contractId: string; + templateId: string; + synchronizerId: string; + /** Party that can see the contract in ACS (typically transferAgent). */ + readAsParty: string; + } +): Promise { + const response = await ledger.getActiveContracts({ + parties: [params.readAsParty], + templateIds: [params.templateId], + includeCreatedEventBlob: true, + }); + for (const item of response) { + const entry = item.contractEntry; + if (!('JsActiveContract' in entry)) continue; + const created = entry.JsActiveContract.createdEvent; + if (created.contractId !== params.contractId) continue; + requireTemplateIdMatch({ + contractId: params.contractId, + actualTemplateId: created.templateId, + expectedTemplateId: params.templateId, + }); + const blob = + created.createdEventBlob ?? + (await fetchCreatedEventBlob(ledger, params.contractId, [params.readAsParty])); + return { + templateId: created.templateId, + contractId: params.contractId, + createdEventBlob: blob, + synchronizerId: params.synchronizerId, + }; + } + + const disclosure = await fetchCreatedEventDisclosure(ledger, params.contractId, [ + params.readAsParty, + ]); + requireTemplateIdMatch({ + contractId: params.contractId, + actualTemplateId: disclosure.templateId, + expectedTemplateId: params.templateId, + }); + return { + ...disclosure, + synchronizerId: disclosure.synchronizerId || params.synchronizerId, + }; +} diff --git a/test/integration/localnet/cip56-transfer.test.ts b/test/integration/localnet/cip56-transfer.test.ts new file mode 100644 index 0000000..80319f5 --- /dev/null +++ b/test/integration/localnet/cip56-transfer.test.ts @@ -0,0 +1,138 @@ +/** + * CIP-56 / CIP-112 LocalNet transfer smoke (Splice TestTokenV2). + * + * Sequence (ENG-1635): + * 1. fetch — `npm run fixture:splice-test-token-v2:fetch` + * 2. validate — assert DAR SHA-256 + package id constants + * 3. upload/vet — LOCALNET: upload DAR to Ledger JSON API + vet packages + * 4. parties — LOCALNET: allocate admin / alice / bob (createParty amount 0) + * 5. TokenRules — create TokenRules for admin + * 6. mint — TokenRules_OfferMint + TransferInstruction_Accept (receiver) + * 7. transfer — TransferFactory_Transfer (V2) + TransferInstruction_Accept + * 8. assert — LOCALNET: receiver holdings / balances via listTokenStandardV2Holdings + * + * Unit-safe steps run without LocalNet. When Ledger is unreachable on :3975 the suite + * skips (unit CI) rather than fail; `npm run localnet:cip56-transfer` fails closed via + * `CANTON_CIP56_REQUIRE_LOCALNET=1`. + */ + +import { + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + buildTokenRulesCreateCommand, + buildTokenRulesOfferMintCommand, + buildTransferFactoryTransferCommand, + buildTransferInstructionAcceptCommand, + encodeProviderLessExtraArgs, + isSpliceTestTokenV2DarPresent, + parseTransferInstructionResult, + providerLessAccount, + runCip56TransferSmoke, + spliceTestTokenV2Instrument, + toCreateCommand, + toExerciseCommand, +} from '../../../src/testing'; + +const LEDGER_URL = process.env['FAIRMINT_TEST_LEDGER_API_URL'] ?? 'http://localhost:3975'; +const PACKAGE_ROOT = process.cwd(); +const REQUIRE_LOCALNET = process.env['CANTON_CIP56_REQUIRE_LOCALNET'] === '1'; + +async function isLedgerReachable(): Promise { + try { + const response = await fetch(`${LEDGER_URL.replace(/\/$/, '')}/v2/version`, { + signal: AbortSignal.timeout(2_000), + }); + // Match LocalNet readiness: oauth2 Ledger returns 401 without a token. + return response.ok || response.status === 401; + } catch { + return false; + } +} + +describe('CIP-56 / CIP-112 Splice TestTokenV2 LocalNet transfer', (): void => { + it('validates fixture constants and command builders without LocalNet', (): void => { + expect(SPLICE_TEST_TOKEN_V2_SHA256).toHaveLength(64); + expect(SPLICE_TEST_TOKEN_V2_PACKAGE_ID).toHaveLength(64); + + const create = buildTokenRulesCreateCommand({ admin: 'Admin::party' }); + const mint = buildTokenRulesOfferMintCommand({ + tokenRulesContractId: 'cid-rules', + admin: 'Admin::party', + receiver: 'Alice::party', + amount: '50.0', + offeredAt: '2026-01-01T00:00:00Z', + }); + const transfer = buildTransferFactoryTransferCommand({ + transferFactoryContractId: 'cid-rules', + admin: 'Admin::party', + sender: 'Alice::party', + receiver: 'Bob::party', + amount: '10.0', + inputHoldingCids: ['cid-holding'], + requestedAt: '2026-01-01T00:00:00Z', + executeBefore: '2026-01-08T00:00:00Z', + extraArgs: encodeProviderLessExtraArgs({ tokenRulesContractId: 'cid-rules' }), + }); + const accept = buildTransferInstructionAcceptCommand({ + transferInstructionContractId: 'cid-instr', + actors: ['Bob::party'], + extraArgs: encodeProviderLessExtraArgs({ tokenRulesContractId: 'cid-rules' }), + }); + + expect(create.createArgument['admin']).toBe('Admin::party'); + expect(mint.choiceArgument['receiver']).toEqual(providerLessAccount('Alice::party')); + expect(transfer.choiceArgument['transfer']).toMatchObject({ + instrumentId: spliceTestTokenV2Instrument('Admin::party'), + amount: '10', + }); + expect(accept.choice).toBe('TransferInstruction_Accept'); + + expect(toCreateCommand(create)).toEqual({ + CreateCommand: { + templateId: create.templateId, + createArguments: create.createArgument, + }, + }); + expect(toExerciseCommand(accept).ExerciseCommand?.choice).toBe('TransferInstruction_Accept'); + + expect( + parseTransferInstructionResult({ + output: { + tag: 'TransferInstructionResult_Pending', + value: { transferInstructionCid: 'cid-pending' }, + }, + senderChangeCids: [], + meta: { values: {} }, + }) + ).toEqual({ type: 'Pending', transferInstructionCid: 'cid-pending' }); + }); + + it('runs upload/vet/parties/mint/transfer/assert against LocalNet when Ledger is up', async (): Promise => { + const reachable = await isLedgerReachable(); + + if (!reachable) { + if (REQUIRE_LOCALNET) { + throw new Error( + `LocalNet Ledger not reachable at ${LEDGER_URL}/v2/version (CANTON_CIP56_REQUIRE_LOCALNET=1)` + ); + } + console.warn( + `[skip] LocalNet Ledger not reachable at ${LEDGER_URL}; leaving upload/vet/parties/mint/transfer for Docker path` + ); + return; + } + + expect(isSpliceTestTokenV2DarPresent(PACKAGE_ROOT)).toBe(true); + + const result = await runCip56TransferSmoke({ packageRoot: PACKAGE_ROOT }); + + expect(result.adminPartyId.length).toBeGreaterThan(0); + expect(result.alicePartyId.length).toBeGreaterThan(0); + expect(result.bobPartyId.length).toBeGreaterThan(0); + expect(result.tokenRulesContractId.length).toBeGreaterThan(0); + expect(result.aliceBalance).toBe('40'); + expect(result.bobBalance).toBe('10'); + expect(result.mintOfferContractId.length).toBeGreaterThan(0); + expect(result.transferInstructionContractId.length).toBeGreaterThan(0); + }, 300_000); +}); diff --git a/test/unit/cip56/transferSmokeTraffic.test.ts b/test/unit/cip56/transferSmokeTraffic.test.ts new file mode 100644 index 0000000..2eda605 --- /dev/null +++ b/test/unit/cip56/transferSmokeTraffic.test.ts @@ -0,0 +1,24 @@ +import { + collectErrorMessages, + isTransientValidatorTrafficError, +} from '../../../src/testing/cip56/transferSmoke'; + +describe('CIP-56 transfer smoke traffic error classification', (): void => { + it('detects HTTP 429 and traffic-balance messages in nested causes', (): void => { + const cause = new Error('ApiError: HTTP 429 [request: post http://localhost:3903/api/validator/v0/admin/users]'); + const wrapped = new Error( + 'UnknownMutationOutcomeError: The outcome of POST http://localhost:3903/api/validator/v0/admin/users is unknown after 1 request attempt', + { cause } + ); + expect(isTransientValidatorTrafficError(wrapped)).toBe(true); + expect(collectErrorMessages(wrapped)).toContain('HTTP 429'); + + const traffic = new Error('ABORTED: Traffic balance below reserved traffic amount (0 < 200000)'); + expect(isTransientValidatorTrafficError(traffic)).toBe(true); + }); + + it('does not treat unrelated failures as traffic transients', (): void => { + expect(isTransientValidatorTrafficError(new Error('HTTP 401 Unauthorized'))).toBe(false); + expect(isTransientValidatorTrafficError(new Error('connection refused'))).toBe(false); + }); +}); diff --git a/test/unit/scripts/canton-dev-tools.test.ts b/test/unit/scripts/canton-dev-tools.test.ts new file mode 100644 index 0000000..9d4c604 --- /dev/null +++ b/test/unit/scripts/canton-dev-tools.test.ts @@ -0,0 +1,136 @@ +import { execFileSync } from 'node:child_process'; +import { + chmodSync, + copyFileSync, + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import { resolve } from 'node:path'; + +const REPO_ROOT = resolve(__dirname, '../../..'); +const PACKAGE_JSON = JSON.parse(readFileSync(resolve(REPO_ROOT, 'package.json'), 'utf8')) as { + config: { + localnet_quickstart_ref: string; + localnet_splice_version: string; + localnet_scribe_version: string; + localnet_protocol_version: string; + }; +}; + +const DEFAULT_QUICKSTART_REF = PACKAGE_JSON.config.localnet_quickstart_ref; +const DEFAULT_SPLICE_VERSION = PACKAGE_JSON.config.localnet_splice_version; +const DEFAULT_SCRIBE_VERSION = PACKAGE_JSON.config.localnet_scribe_version; +const DEFAULT_PROTOCOL_VERSION = PACKAGE_JSON.config.localnet_protocol_version; + +function withPackagedBin( + localnetScriptBody: string, + run: (localnetBin: string, packageRoot: string) => string, + options: { spliceVersionFile?: string } = {} +): string { + const packageRoot = mkdtempSync(resolve(tmpdir(), 'canton-dev-tools-bin-')); + const localnetBin = resolve(packageRoot, 'bin/canton-dev-tools'); + + mkdirSync(resolve(packageRoot, 'bin'), { recursive: true }); + mkdirSync(resolve(packageRoot, 'scripts'), { recursive: true }); + copyFileSync(resolve(REPO_ROOT, 'bin/canton-dev-tools'), localnetBin); + chmodSync(localnetBin, 0o755); + writeFileSync(resolve(packageRoot, 'scripts/localnet-cloud.sh'), localnetScriptBody); + + if (options.spliceVersionFile !== undefined) { + mkdirSync(resolve(packageRoot, 'libs/splice'), { recursive: true }); + writeFileSync(resolve(packageRoot, 'libs/splice/VERSION'), options.spliceVersionFile); + } + + try { + return run(localnetBin, packageRoot); + } finally { + rmSync(packageRoot, { recursive: true, force: true }); + } +} + +function runPackagedLocalnetWithVersion(version: string): string { + return withPackagedBin( + 'printf "%s" "${CANTON_LOCALNET_SPLICE_VERSION}"\n', + (localnetBin) => + execFileSync(localnetBin, ['status'], { + encoding: 'utf8', + env: { + ...process.env, + CANTON_LOCALNET_SPLICE_VERSION: '', + }, + }), + { spliceVersionFile: version } + ); +} + +describe('canton-dev-tools pin ownership', (): void => { + it('hardcodes package.json config pins in the packaged binary', (): void => { + const localnetBin = readFileSync(resolve(REPO_ROOT, 'bin/canton-dev-tools'), 'utf8'); + + expect(localnetBin).toContain(`DEFAULT_QUICKSTART_REF="${DEFAULT_QUICKSTART_REF}"`); + expect(localnetBin).toContain(`DEFAULT_SPLICE_VERSION="${DEFAULT_SPLICE_VERSION}"`); + expect(localnetBin).toContain(`DEFAULT_SCRIBE_VERSION="${DEFAULT_SCRIBE_VERSION}"`); + expect(localnetBin).toContain(`DEFAULT_PROTOCOL_VERSION="${DEFAULT_PROTOCOL_VERSION}"`); + }); + + it('exports all four LocalNet pins for npx / direct binary invocations', (): void => { + const output = withPackagedBin( + [ + 'printf "splice=%s\\n" "${CANTON_LOCALNET_SPLICE_VERSION}"', + 'printf "scribe=%s\\n" "${CANTON_LOCALNET_SCRIBE_VERSION}"', + 'printf "protocol=%s\\n" "${CANTON_LOCALNET_PROTOCOL_VERSION}"', + ].join('\n'), + (localnetBin, packageRoot) => + execFileSync(localnetBin, ['status'], { + encoding: 'utf8', + env: { + ...process.env, + CANTON_LOCALNET_CACHE_DIR: resolve(packageRoot, 'cache'), + HOME: resolve(packageRoot, 'home'), + CANTON_LOCALNET_SPLICE_VERSION: '', + CANTON_LOCALNET_SCRIBE_VERSION: '', + CANTON_LOCALNET_PROTOCOL_VERSION: '', + }, + }) + ); + + expect(output.trim().split('\n')).toEqual([ + `splice=${DEFAULT_SPLICE_VERSION}`, + `scribe=${DEFAULT_SCRIBE_VERSION}`, + `protocol=${DEFAULT_PROTOCOL_VERSION}`, + ]); + }); + + it('uses a non-empty trimmed packaged Splice version', (): void => { + expect(runPackagedLocalnetWithVersion(' 1.2.3 \n')).toBe('1.2.3'); + }); + + it('falls back to the built-in Splice version when the packaged version is blank', (): void => { + expect(runPackagedLocalnetWithVersion(' \n\t')).toBe(DEFAULT_SPLICE_VERSION); + }); +}); + +describe('canton-dev-tools command aliases', (): void => { + it.each([ + ['readiness', 'smoke'], + ['diagnostics', 'logs'], + ['teardown', 'stop'], + ] as const)('maps %s to %s for the downstream script', (productCommand, legacyCommand): void => { + const output = withPackagedBin('printf "%s" "$1"\n', (localnetBin, packageRoot) => + execFileSync(localnetBin, [productCommand], { + encoding: 'utf8', + env: { + ...process.env, + CANTON_LOCALNET_CACHE_DIR: resolve(packageRoot, 'cache'), + HOME: resolve(packageRoot, 'home'), + }, + }) + ); + + expect(output).toBe(legacyCommand); + }); +}); diff --git a/test/unit/scripts/localnet-cloud.test.ts b/test/unit/scripts/localnet-cloud.test.ts new file mode 100644 index 0000000..4796019 --- /dev/null +++ b/test/unit/scripts/localnet-cloud.test.ts @@ -0,0 +1,173 @@ +import { execFileSync } from 'node:child_process'; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { resolve } from 'node:path'; + +const REPO_ROOT = resolve(__dirname, '../../..'); + +function runSourcedLocalnetScript(body: string, args: readonly string[] = []): string { + return execFileSync( + 'bash', + ['-c', `source scripts/localnet-cloud.sh\n${body}`, 'localnet-cloud-test', ...args], + { + cwd: REPO_ROOT, + encoding: 'utf8', + } + ); +} + +describe('localnet-cloud configuration lifecycle', (): void => { + it('persists a pending forced-snapshot restart across setup and start processes', (): void => { + const quickstart = mkdtempSync(resolve(tmpdir(), 'canton-localnet-config-')); + const localnet = resolve(quickstart, 'docker/modules/localnet'); + const cantonConfig = resolve(localnet, 'conf/canton/app.conf'); + const spliceConfig = resolve(localnet, 'conf/splice/sv/app.conf'); + const cantonHealthcheck = resolve(localnet, 'docker/canton/health-check.sh'); + const spliceHealthcheck = resolve(localnet, 'docker/splice/health-check.sh'); + + mkdirSync(resolve(localnet, 'conf/canton'), { recursive: true }); + mkdirSync(resolve(localnet, 'conf/splice/sv'), { recursive: true }); + mkdirSync(resolve(localnet, 'docker/canton'), { recursive: true }); + mkdirSync(resolve(localnet, 'docker/splice'), { recursive: true }); + writeFileSync(cantonConfig, 'canton {}\n'); + writeFileSync(spliceConfig, 'canton {}\n'); + writeFileSync(cantonHealthcheck, '#!/bin/bash\n'); + writeFileSync(spliceHealthcheck, '#!/bin/bash\n'); + writeFileSync(resolve(quickstart, '.env.local'), 'AUTH_MODE=oauth2\n'); + + try { + const setupOutput = runSourcedLocalnetScript( + 'QUICKSTART_DIR="$1"\n' + + 'configure_quickstart_localnet\n' + + 'printf "%s\\n" "${SPLICE_CONFIG_CHANGED}"', + [quickstart] + ); + const startOutput = runSourcedLocalnetScript( + 'QUICKSTART_DIR="$1"\n' + + 'configure_quickstart_localnet\n' + + 'printf "pending:%s\\n" "${SPLICE_CONFIG_CHANGED}"\n' + + 'quickstart_force_full_start() { return 1; }\n' + + 'quickstart_infra_only_enabled() { return 0; }\n' + + 'splice_container_running() { return 0; }\n' + + "start_infra_only_localnet() { printf 'start-infra\\n'; }\n" + + 'run_infra_compose() { printf \'recreate:%s\\n\' "$1"; }\n' + + "wait_for_services() { printf 'wait\\n'; }\n" + + 'start_localnet\n' + + 'printf "applied:%s:%s\\n" "${SPLICE_CONFIG_CHANGED}" "$(quickstart_env_value "${SPLICE_CONFIG_PENDING_KEY}")"', + [quickstart] + ); + + expect(setupOutput.trim()).toBe('true'); + expect(startOutput.trim().split('\n')).toEqual([ + 'pending:true', + 'start-infra', + '[localnet] Recreating the running Splice service to apply updated LocalNet configuration.', + 'recreate:up -d --no-deps --force-recreate splice', + 'wait', + 'applied:false:false', + ]); + const configured = readFileSync(spliceConfig, 'utf8'); + expect( + configured.match(/canton\.scan-apps\.scan-app\.enable-forced-acs-snapshots = true/g) + ).toHaveLength(1); + } finally { + rmSync(quickstart, { recursive: true, force: true }); + } + }); + + it('recreates a previously running Splice service after infra-only startup and before readiness', (): void => { + const output = runSourcedLocalnetScript(` +quickstart_force_full_start() { return 1; } +quickstart_infra_only_enabled() { return 0; } +splice_container_running() { return 0; } +start_infra_only_localnet() { printf 'start-infra\\n'; } +run_infra_compose() { printf 'recreate:%s\\n' "$1"; } +wait_for_services() { printf 'wait\\n'; } +set_quickstart_env_value() { :; } +SPLICE_CONFIG_CHANGED=true +start_localnet +`); + + expect(output.trim().split('\n')).toEqual([ + 'start-infra', + '[localnet] Recreating the running Splice service to apply updated LocalNet configuration.', + 'recreate:up -d --no-deps --force-recreate splice', + 'wait', + ]); + }); + + it('recreates a previously running Splice service after fast startup and before readiness', (): void => { + const output = runSourcedLocalnetScript(` +quickstart_force_full_start() { return 1; } +quickstart_infra_only_enabled() { return 1; } +quickstart_fast_start_enabled() { return 0; } +quickstart_build_artifacts_ready() { return 0; } +try_fast_start_localnet() { printf 'start-fast\\n'; } +splice_container_running() { return 0; } +run_infra_compose() { printf 'recreate:%s\\n' "$1"; } +wait_for_services() { printf 'wait\\n'; } +set_quickstart_env_value() { :; } +SPLICE_CONFIG_CHANGED=true +start_localnet +`); + + expect(output.trim().split('\n')).toEqual([ + 'start-fast', + '[localnet] Recreating the running Splice service to apply updated LocalNet configuration.', + 'recreate:up -d --no-deps --force-recreate splice', + 'wait', + ]); + }); + + it('writes Canton/Splice healthchecks with valid errexit/nounset/pipefail flags', (): void => { + const quickstart = mkdtempSync(resolve(tmpdir(), 'canton-localnet-healthcheck-')); + const localnet = resolve(quickstart, 'docker/modules/localnet'); + const cantonHealthcheck = resolve(localnet, 'docker/canton/health-check.sh'); + const spliceHealthcheck = resolve(localnet, 'docker/splice/health-check.sh'); + + mkdirSync(resolve(localnet, 'conf/canton'), { recursive: true }); + mkdirSync(resolve(localnet, 'conf/splice/sv'), { recursive: true }); + mkdirSync(resolve(localnet, 'docker/canton'), { recursive: true }); + mkdirSync(resolve(localnet, 'docker/splice'), { recursive: true }); + writeFileSync(resolve(localnet, 'conf/canton/app.conf'), 'canton {}\n'); + writeFileSync(resolve(localnet, 'conf/splice/sv/app.conf'), 'canton {}\n'); + writeFileSync(cantonHealthcheck, '#!/bin/bash\n'); + writeFileSync(spliceHealthcheck, '#!/bin/bash\n'); + writeFileSync(resolve(quickstart, '.env.local'), 'AUTH_MODE=oauth2\n'); + + try { + runSourcedLocalnetScript('QUICKSTART_DIR="$1"\nconfigure_quickstart_localnet\n', [ + quickstart, + ]); + + for (const healthcheck of [cantonHealthcheck, spliceHealthcheck]) { + const body = readFileSync(healthcheck, 'utf8'); + expect(body).toMatch(/^set -euo pipefail$/m); + expect(body).not.toMatch(/set -eou pipefail/); + } + } finally { + rmSync(quickstart, { recursive: true, force: true }); + } + }); + + it('does not recreate Splice for a fresh stack or unchanged configuration', (): void => { + const output = runSourcedLocalnetScript(` +quickstart_force_full_start() { return 1; } +quickstart_infra_only_enabled() { return 0; } +start_infra_only_localnet() { printf 'start-infra\\n'; } +run_infra_compose() { printf 'unexpected-recreate:%s\\n' "$1"; } +wait_for_services() { printf 'wait\\n'; } +set_quickstart_env_value() { :; } + +splice_container_running() { return 1; } +SPLICE_CONFIG_CHANGED=true +start_localnet + +splice_container_running() { return 0; } +SPLICE_CONFIG_CHANGED=false +start_localnet +`); + + expect(output.trim().split('\n')).toEqual(['start-infra', 'wait', 'start-infra', 'wait']); + }); +}); diff --git a/test/unit/scripts/package-artifacts.test.ts b/test/unit/scripts/package-artifacts.test.ts new file mode 100644 index 0000000..82ccece --- /dev/null +++ b/test/unit/scripts/package-artifacts.test.ts @@ -0,0 +1,65 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const REPO_ROOT = resolve(__dirname, '../../..'); + +/** + * Mirrors scripts/check-package-artifacts.ts forbiddenPackagePathReason so unit CI + * catches regressions without invoking npm pack on every jest run. + */ +function forbiddenPackagePathReason(packagePath: string): string | null { + if (packagePath.endsWith('.dar')) return 'DAML DAR files are not runtime package artifacts'; + if (packagePath === 'fixtures' || packagePath.startsWith('fixtures/')) { + return 'internal fixtures (including DAR lifecycle sources) must not be published'; + } + if (packagePath === 'internal' || packagePath.startsWith('internal/')) { + return 'internal CI-only assets must not be published'; + } + if (packagePath === 'libs' || packagePath.startsWith('libs/')) { + return 'submodules under libs/ must not be published'; + } + if (packagePath === 'node_modules' || packagePath.startsWith('node_modules/')) { + return 'node_modules must not be published'; + } + if (packagePath === 'core' || packagePath.startsWith('core.')) { + return 'crash dumps must not be published'; + } + return null; +} + +describe('package artifact policy', (): void => { + it('publishes only bin, scripts/localnet-cloud.sh, and dist/**', (): void => { + const pkg = JSON.parse(readFileSync(resolve(REPO_ROOT, 'package.json'), 'utf8')) as { + files: string[]; + }; + + expect(pkg.files).toEqual(['bin/canton-dev-tools', 'scripts/localnet-cloud.sh', 'dist/**']); + expect(pkg.files.join('\n')).not.toMatch(/fixtures|internal|\.dar/); + }); + + it('forbids shipping fixtures and DAR lifecycle paths', (): void => { + expect(forbiddenPackagePathReason('fixtures/dar-lifecycle/daml.yaml')).toMatch(/fixtures/); + expect( + forbiddenPackagePathReason('fixtures/dar-lifecycle/.daml/dist/DarLifecycle-0.0.1.dar') + ).toMatch(/DAR/); + expect( + forbiddenPackagePathReason('fixtures/splice-test-token-v2/splice-test-token-v2-1.0.0.dar') + ).toMatch(/DAR/); + expect(forbiddenPackagePathReason('fixtures/splice-test-token-v2/README.md')).toMatch( + /fixtures/ + ); + expect(forbiddenPackagePathReason('internal/daml-fixture/daml.yaml')).toMatch(/internal/); + expect(forbiddenPackagePathReason('artifact.dar')).toMatch(/DAR/); + expect(forbiddenPackagePathReason('bin/canton-dev-tools')).toBeNull(); + expect(forbiddenPackagePathReason('scripts/localnet-cloud.sh')).toBeNull(); + expect(forbiddenPackagePathReason('dist/index.js')).toBeNull(); + }); + + it('keeps check-package-artifacts aligned with fixture exclusion policy', (): void => { + const checker = readFileSync(resolve(REPO_ROOT, 'scripts/check-package-artifacts.ts'), 'utf8'); + expect(checker).toContain("packagePath === 'fixtures'"); + expect(checker).toContain("packagePath.startsWith('fixtures/')"); + expect(checker).toContain('DEFAULT_SCRIBE_VERSION'); + expect(checker).toContain('DEFAULT_PROTOCOL_VERSION'); + }); +}); diff --git a/test/unit/scripts/prepare-release.test.ts b/test/unit/scripts/prepare-release.test.ts new file mode 100644 index 0000000..59fbd9f --- /dev/null +++ b/test/unit/scripts/prepare-release.test.ts @@ -0,0 +1,30 @@ +import { selectReleaseVersion } from '../../../scripts/prepare-release'; + +describe('selectReleaseVersion', (): void => { + const withTakenVersions = (...versions: string[]) => { + const takenVersions = new Set(versions); + return (version: string): boolean => takenVersions.has(version); + }; + + it('publishes package.json exactly on first publish (npm 404)', (): void => { + expect(selectReleaseVersion('0.1.0', null, withTakenVersions())).toBe('0.1.0'); + }); + + it('publishes an intentionally advanced manifest version unchanged', (): void => { + expect(selectReleaseVersion('0.6.0', '0.5.23', withTakenVersions('0.5.23'))).toBe('0.6.0'); + }); + + it('resumes patch increments after the manifest version has been published', (): void => { + expect(selectReleaseVersion('0.6.0', '0.6.0', withTakenVersions('0.6.0'))).toBe('0.6.1'); + }); + + it('increments from a newer NPM baseline and skips unavailable versions', (): void => { + expect(selectReleaseVersion('0.6.0', '0.6.2', withTakenVersions('0.6.2', '0.6.3'))).toBe( + '0.6.4' + ); + }); + + it('does not reuse an advanced manifest version that is already tagged', (): void => { + expect(selectReleaseVersion('0.6.0', '0.5.23', withTakenVersions('0.6.0'))).toBe('0.6.1'); + }); +}); diff --git a/test/unit/testing/cip56.test.ts b/test/unit/testing/cip56.test.ts new file mode 100644 index 0000000..9dbdd0f --- /dev/null +++ b/test/unit/testing/cip56.test.ts @@ -0,0 +1,227 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { + ACCOUNT_CONFIGS_CONTEXT_KEY, + SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID, + SPLICE_TEST_TOKEN_V2_PACKAGE_ID, + SPLICE_TEST_TOKEN_V2_SHA256, + TOKEN_RULES_CONTEXT_KEY, + TOKEN_RULES_TEMPLATE_ID, + TRANSFER_FACTORY_INTERFACE_ID, + TRANSFER_INSTRUCTION_INTERFACE_ID, + buildTokenRulesCreateCommand, + buildTokenRulesOfferMintCommand, + buildTransferFactoryTransferCommand, + buildTransferInstructionAcceptCommand, + encodeProviderLessExtraArgs, + formatDamlNumeric, + parseTransferInstructionResult, + providerLessAccount, + providerLessAccountConfig, + spliceTestTokenV2Instrument, + toCreateCommand, + toExerciseCommand, +} from '../../../src/testing'; + +const REPO_ROOT = resolve(__dirname, '../../..'); + +/** + * Mirrors scripts/check-package-artifacts.ts forbiddenPackagePathReason so unit CI + * catches regressions without invoking npm pack on every jest run. + */ +function forbiddenPackagePathReason(packagePath: string): string | null { + if (packagePath.endsWith('.dar')) return 'DAML DAR files are not runtime package artifacts'; + if (packagePath === 'fixtures' || packagePath.startsWith('fixtures/')) { + return 'internal fixtures (including DAR lifecycle sources) must not be published'; + } + if (packagePath === 'internal' || packagePath.startsWith('internal/')) { + return 'internal CI-only assets must not be published'; + } + if (packagePath === 'libs' || packagePath.startsWith('libs/')) { + return 'submodules under libs/ must not be published'; + } + if (packagePath === 'node_modules' || packagePath.startsWith('node_modules/')) { + return 'node_modules must not be published'; + } + if (packagePath === 'core' || packagePath.startsWith('core.')) { + return 'crash dumps must not be published'; + } + return null; +} + +describe('CIP-56 / CIP-112 Splice TestTokenV2 helpers', (): void => { + it('pins the expected DAR SHA-256 and package id constants', (): void => { + expect(SPLICE_TEST_TOKEN_V2_SHA256).toBe( + '43fcf2fcf4e84861501a0c00e8550e2863e1aad553b1fb772ee8aa7bca7fd245' + ); + expect(SPLICE_TEST_TOKEN_V2_PACKAGE_ID).toBe( + 'a38a96b6f46c14c599b2763bc4fc68911a9cada90f89c599a1401e8e3df685e1' + ); + expect(SPLICE_TEST_TOKEN_V2_INSTRUMENT_ID).toBe('X2'); + }); + + it('documents that pack:check forbids fixtures/** and *.dar', (): void => { + expect(forbiddenPackagePathReason('fixtures/splice-test-token-v2/README.md')).toMatch( + /fixtures/ + ); + expect( + forbiddenPackagePathReason('fixtures/splice-test-token-v2/splice-test-token-v2-1.0.0.dar') + ).toMatch(/DAR/); + expect(forbiddenPackagePathReason('splice-test-token-v2-1.0.0.dar')).toMatch(/DAR/); + + const checker = readFileSync(resolve(REPO_ROOT, 'scripts/check-package-artifacts.ts'), 'utf8'); + expect(checker).toContain("packagePath === 'fixtures'"); + expect(checker).toContain("packagePath.startsWith('fixtures/')"); + expect(checker).toContain(".endsWith('.dar')"); + + const pkg = JSON.parse(readFileSync(resolve(REPO_ROOT, 'package.json'), 'utf8')) as { + files: string[]; + }; + expect(pkg.files.join('\n')).not.toMatch(/fixtures|\.dar/); + }); + + it('builds provider-less accounts and AccountConfig records', (): void => { + expect(providerLessAccount('Alice::party')).toEqual({ + owner: 'Alice::party', + provider: null, + id: '', + }); + expect(providerLessAccountConfig('Admin::party', 'Alice::party')).toEqual({ + admin: 'Admin::party', + account: { owner: 'Alice::party', provider: null, id: '' }, + ownerConfig: { canInitiate: true, mustApprove: true }, + providerConfig: { canInitiate: false, mustApprove: false }, + }); + expect(spliceTestTokenV2Instrument('Admin::party')).toEqual({ + admin: 'Admin::party', + id: 'X2', + }); + }); + + it('encodes ExtraArgs with TokenRules context for provider-less flows', (): void => { + const extra = encodeProviderLessExtraArgs({ + tokenRulesContractId: 'cid-token-rules', + meta: { reason: 'unit-test' }, + }); + expect(extra.context.values[TOKEN_RULES_CONTEXT_KEY]).toEqual({ + tag: 'AV_ContractId', + value: 'cid-token-rules', + }); + expect(extra.context.values[ACCOUNT_CONFIGS_CONTEXT_KEY]).toBeUndefined(); + expect(extra.meta.values).toEqual({ reason: 'unit-test' }); + }); + + it('builds TokenRules create, OfferMint, TransferFactory_Transfer, and Accept commands', (): void => { + const create = buildTokenRulesCreateCommand({ admin: 'Admin::party' }); + expect(create.templateId).toBe(TOKEN_RULES_TEMPLATE_ID); + expect(create.createArgument).toEqual({ admin: 'Admin::party' }); + + const mint = buildTokenRulesOfferMintCommand({ + tokenRulesContractId: 'cid-rules', + admin: 'Admin::party', + receiver: 'Alice::party', + amount: '100.0', + offeredAt: '2026-01-01T00:00:00Z', + }); + expect(mint.choice).toBe('TokenRules_OfferMint'); + expect(mint.choiceArgument['instrumentId']).toEqual({ + admin: 'Admin::party', + id: 'X2', + }); + expect(mint.choiceArgument['receiver']).toEqual(providerLessAccount('Alice::party')); + expect(mint.choiceArgument['amount']).toBe('100'); + + const tinyMint = buildTokenRulesOfferMintCommand({ + tokenRulesContractId: 'cid-rules', + admin: 'Admin::party', + receiver: 'Alice::party', + amount: 0.0000001, + offeredAt: '2026-01-01T00:00:00Z', + }); + expect(tinyMint.choiceArgument['amount']).toBe('0.0000001'); + expect(formatDamlNumeric(0.0000001)).toBe('0.0000001'); + expect(formatDamlNumeric('1.2500000000')).toBe('1.25'); + expect(() => formatDamlNumeric(0.00000000001)).toThrow(/cannot be represented exactly/); + + const transfer = buildTransferFactoryTransferCommand({ + transferFactoryContractId: 'cid-rules', + admin: 'Admin::party', + sender: 'Alice::party', + receiver: 'Bob::party', + amount: 10, + inputHoldingCids: ['cid-holding-1'], + requestedAt: '2026-01-01T00:00:00Z', + executeBefore: '2026-01-08T00:00:00Z', + extraArgs: encodeProviderLessExtraArgs({ tokenRulesContractId: 'cid-rules' }), + }); + expect(transfer.templateId).toBe(TRANSFER_FACTORY_INTERFACE_ID); + expect(transfer.choice).toBe('TransferFactory_Transfer'); + expect(transfer.choiceArgument['actors']).toEqual(['Alice::party']); + expect((transfer.choiceArgument['transfer'] as { amount: string }).amount).toBe('10'); + + const fractionalTransfer = buildTransferFactoryTransferCommand({ + transferFactoryContractId: 'cid-rules', + admin: 'Admin::party', + sender: 'Alice::party', + receiver: 'Bob::party', + amount: '0.5', + inputHoldingCids: ['cid-holding-1'], + requestedAt: '2026-01-01T00:00:00Z', + executeBefore: '2026-01-08T00:00:00Z', + }); + expect((fractionalTransfer.choiceArgument['transfer'] as { amount: string }).amount).toBe( + '0.5' + ); + const accept = buildTransferInstructionAcceptCommand({ + transferInstructionContractId: 'cid-instr', + actors: ['Bob::party'], + extraArgs: encodeProviderLessExtraArgs({ tokenRulesContractId: 'cid-rules' }), + }); + expect(accept.templateId).toBe(TRANSFER_INSTRUCTION_INTERFACE_ID); + expect(accept.choice).toBe('TransferInstruction_Accept'); + expect(accept.choiceArgument['actors']).toEqual(['Bob::party']); + }); + + it('parses TransferInstructionResult JSON and wraps ledger commands', (): void => { + expect( + parseTransferInstructionResult({ + output: { + tag: 'TransferInstructionResult_Completed', + value: { receiverHoldingCids: ['h1'] }, + }, + senderChangeCids: [], + meta: { values: {} }, + }) + ).toEqual({ type: 'Completed', receiverHoldingCids: ['h1'] }); + + expect( + parseTransferInstructionResult({ + output: { tag: 'TransferInstructionResult_Pending', value: 'cid-direct' }, + senderChangeCids: [], + meta: { values: {} }, + }) + ).toEqual({ type: 'Pending', transferInstructionCid: 'cid-direct' }); + + const create = buildTokenRulesCreateCommand({ admin: 'Admin::party' }); + expect(toCreateCommand(create)).toEqual({ + CreateCommand: { + templateId: TOKEN_RULES_TEMPLATE_ID, + createArguments: { admin: 'Admin::party' }, + }, + }); + + const accept = buildTransferInstructionAcceptCommand({ + transferInstructionContractId: 'cid-instr', + actors: ['Bob::party'], + }); + expect(toExerciseCommand(accept)).toEqual({ + ExerciseCommand: { + templateId: TRANSFER_INSTRUCTION_INTERFACE_ID, + contractId: 'cid-instr', + choice: 'TransferInstruction_Accept', + choiceArgument: accept.choiceArgument, + }, + }); + }); +}); diff --git a/test/unit/testing/helpers.test.ts b/test/unit/testing/helpers.test.ts new file mode 100644 index 0000000..a332b95 --- /dev/null +++ b/test/unit/testing/helpers.test.ts @@ -0,0 +1,69 @@ +import { + buildIntegrationTestClientConfig, + buildLocalnetClientConfig, + createSharedSecretJwt, + generateTestId, + findCreatedContractId, + listCreatedEvents, +} from '../../../src/testing'; + +describe('testing helpers', (): void => { + const originalEnv = { ...process.env }; + + afterEach((): void => { + process.env = { ...originalEnv }; + }); + + it('builds a localnet client config with SDK OAuth2 defaults', (): void => { + const config = buildLocalnetClientConfig(); + expect(config.network).toBe('localnet'); + expect(config.provider).toBe('app-provider'); + expect(buildIntegrationTestClientConfig()).toEqual(config); + }); + + it('honors Ledger URL overrides without requiring auth overrides', (): void => { + process.env['FAIRMINT_TEST_LEDGER_API_URL'] = 'http://ledger.example:3975'; + delete process.env['FAIRMINT_TEST_AUTH_URL']; + delete process.env['FAIRMINT_TEST_CLIENT_ID']; + delete process.env['FAIRMINT_TEST_SHARED_SECRET']; + + const config = buildLocalnetClientConfig(); + expect(config.apis?.LEDGER_JSON_API?.apiUrl).toBe('http://ledger.example:3975'); + expect(config.provider).toBe('app-provider'); + expect(config.authUrl).toContain('openid-connect/token'); + expect(config.apis?.LEDGER_JSON_API?.auth).toMatchObject({ + grantType: 'password', + clientId: 'app-provider-unsafe', + username: 'app-provider', + }); + expect(config.apis?.LEDGER_JSON_API?.auth).not.toHaveProperty('bearerToken'); + }); + + it('creates a shared-secret JWT and stable test ids', (): void => { + const token = createSharedSecretJwt({ subject: 'ledger-api-user' }); + expect(token.split('.')).toHaveLength(3); + expect(generateTestId('demo')).toMatch(/^demo-\d+-[a-z0-9]+$/); + }); + + it('finds created contract ids from transaction trees', (): void => { + const tree = { + transactionTree: { + eventsById: { + '0': { + CreatedTreeEvent: { + value: { + contractId: 'cid-1', + templateId: '#Pkg:Mod:Holding', + createdEventBlob: 'blob', + createArgument: {}, + }, + }, + }, + }, + }, + }; + + expect(listCreatedEvents(tree)).toHaveLength(1); + expect(findCreatedContractId(tree, 'Holding')).toBe('cid-1'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9daa31a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}